/* Register to Stack convert for GNU compiler.
- Copyright (C) 1992 Free Software Foundation, Inc.
+ Copyright (C) 1992, 1993, 1994 Free Software Foundation, Inc.
This file is part of GNU CC.
/* This is the basic stack record. TOP is an index into REG[] such
that REG[TOP] is the top of stack. If TOP is -1 the stack is empty.
- If TOP is -2 the stack is not yet initialized: reg_set indicates
- which registers are live. Stack initialization consists of placing
- each live reg in array `reg' and setting `top' appropriately. */
+ If TOP is -2, REG[] is not yet initialized. Stack initialization
+ consists of placing each live reg in array `reg' and setting `top'
+ appropriately.
+
+ REG_SET indicates which registers are live. */
typedef struct stack_def
{
later, but only to look up an insn that is the head or tail of some
block. life_analysis and the stack register conversion process can
add insns within a block. */
-static short *block_number;
+static int *block_number;
/* This is the register file for all register after conversion */
static rtx FP_mode_reg[FIRST_PSEUDO_REGISTER][(int) MAX_MACHINE_MODE];
#define BLOCK_NUM(INSN) \
(((INSN_UID (INSN) > max_uid) \
- ? (short *)(abort() , 0) \
+ ? (int *)(abort() , 0) \
: block_number)[INSN_UID (INSN)])
+extern rtx forced_labels;
extern rtx gen_jump ();
-extern rtx gen_movdf ();
+extern rtx gen_movdf (), gen_movxf ();
extern rtx find_regno_note ();
extern rtx emit_jump_insn_before ();
extern rtx emit_label_after ();
/* Forward declarations */
static void find_blocks ();
+static uses_reg_or_mem ();
static void stack_reg_life_analysis ();
static void change_stack ();
static void convert_regs ();
int
stack_regs_mentioned_p (pat)
- register rtx pat;
+ rtx pat;
{
register char *fmt;
register int i;
/* Count the basic blocks. Also find maximum insn uid. */
{
- register RTX_CODE prev_code = JUMP_INSN;
+ register RTX_CODE prev_code = BARRIER;
register RTX_CODE code;
max_uid = 0;
for (insn = first; insn; insn = NEXT_INSN (insn))
{
/* Note that this loop must select the same block boundaries
- as code in find_blocks. */
+ as code in find_blocks. Also note that this code is not the
+ same as that used in flow.c. */
if (INSN_UID (insn) > max_uid)
max_uid = INSN_UID (insn);
|| (prev_code != INSN
&& prev_code != CALL_INSN
&& prev_code != CODE_LABEL
- && (code == INSN || code == CALL_INSN || code == JUMP_INSN)))
+ && GET_RTX_CLASS (code) == 'i'))
blocks++;
/* Remember whether or not this insn mentions an FP regs.
Check JUMP_INSNs too, in case someone creates a funny PARALLEL. */
- if ((GET_CODE (insn) == INSN || GET_CODE (insn) == CALL_INSN
- || GET_CODE (insn) == JUMP_INSN)
+ if (GET_RTX_CLASS (GET_CODE (insn)) == 'i'
&& stack_regs_mentioned_p (PATTERN (insn)))
{
stack_reg_seen = 1;
else
PUT_MODE (insn, VOIDmode);
+ if (code == CODE_LABEL)
+ LABEL_REFS (insn) = insn; /* delete old chain */
+
if (code != NOTE)
prev_code = code;
}
bzero (block_stack_in, blocks * sizeof (struct stack_def));
bzero (block_out_reg_set, blocks * sizeof (HARD_REG_SET));
- block_number = (short *) alloca ((max_uid + 1) * sizeof (short));
+ block_number = (int *) alloca ((max_uid + 1) * sizeof (int));
find_blocks (first);
stack_reg_life_analysis (first);
/* Don't make a duplicate in the code_label's chain. */
- for (ref = LABEL_REFS (label); ref != label; ref = LABEL_NEXTREF (ref))
+ for (ref = LABEL_REFS (label);
+ ref && ref != label;
+ ref = LABEL_NEXTREF (ref))
if (CONTAINING_INSN (ref) == insn)
return;
while (GET_CODE (*pat) == SUBREG
|| GET_CODE (*pat) == FLOAT
|| GET_CODE (*pat) == FIX
- || GET_CODE (*pat) == FLOAT_EXTEND
- || GET_CODE (*pat) == FLOAT_TRUNCATE)
+ || GET_CODE (*pat) == FLOAT_EXTEND)
pat = & XEXP (*pat, 0);
return pat;
}
-
-/* If REG is a stack register that is marked dead in REGSTACK, then
- record that it is now live. If REG is not DEST, add a death note to
- INSN if there isn't one already. If DEST is not a reg, it is safe to
- assume that it does not mention a reg anywhere within. */
-
-static void
-record_note_if_dead (insn, regstack, reg, dest)
- rtx insn;
- stack regstack;
- rtx reg, dest;
-{
- reg = * get_true_reg (& reg);
-
- if (STACK_REG_P (reg))
- {
- if (! TEST_HARD_REG_BIT (regstack->reg_set, REGNO (reg)))
- {
- if ((! REG_P (dest) || REGNO (dest) != REGNO (reg))
- && ! find_regno_note (insn, REG_DEAD, REGNO (reg)))
- REG_NOTES (insn) = gen_rtx (EXPR_LIST,
- REG_DEAD, reg, REG_NOTES (insn));
-
- SET_HARD_REG_BIT (regstack->reg_set, REGNO (reg));
- }
- }
- else
- if (stack_regs_mentioned_p (reg))
- abort ();
-}
\f
/* Scan the OPERANDS and OPERAND_CONSTRAINTS of an asm_operands.
N_OPERANDS is the total number of operands. Return which alternative
rtx *clobber_reg;
- /* Find out what the constraints required. If no constraint
- alternative matches, that is a compiler bug: we should have caught
- such an insn during reload. */
+ /* Find out what the constraints require. If no constraint
+ alternative matches, this asm is malformed. */
i = constrain_asm_operands (n_operands, operands, constraints,
operand_matches, operand_class);
if (i < 0)
- abort ();
+ malformed_asm = 1;
/* Strip SUBREGs here to make the following code simpler. */
for (i = 0; i < n_operands; i++)
bzero (reg_used_as_output, sizeof (reg_used_as_output));
for (i = 0; i < n_outputs; i++)
if (STACK_REG_P (operands[i]))
- if (reg_class_size[operand_class[i]] != 1)
+ if (reg_class_size[(int) operand_class[i]] != 1)
{
error_for_asm
(insn, "Output constraint %d must specify a single register", i);
if (! TEST_HARD_REG_BIT (regstack->reg_set, REGNO (operands[i]))
&& operand_matches[i] == -1
- && ! find_regno_note (insn, REG_DEAD, REGNO (operands[i])))
+ && find_regno_note (insn, REG_DEAD, REGNO (operands[i])) == NULL_RTX)
REG_NOTES (insn) = gen_rtx (EXPR_LIST, REG_DEAD, operands[i],
REG_NOTES (insn));
}
}
-/* Scan PAT, which is part of INSN, and record the life & death of
- stack registers in REGSTACK. If a register was dead, but is an input
- operand in this insn, then mark the register live and record a death
- note.
-
- If a register is dead after this insn, but is an output operand in
- this insn, record a REG_UNUSED note.
+/* Scan PAT, which is part of INSN, and record registers appearing in
+ a SET_DEST in DEST, and other registers in SRC.
This function does not know about SET_DESTs that are both input and
output (such as ZERO_EXTRACT) - this cannot happen on a 387. */
-static void
-record_reg_life_pat (insn, regstack, pat)
- rtx insn;
- stack regstack;
+void
+record_reg_life_pat (pat, src, dest)
rtx pat;
+ HARD_REG_SET *src, *dest;
{
- rtx src, dest;
-
- /* We should have already handled any asm. */
- if (GET_CODE (pat) == ASM_INPUT || GET_CODE (pat) == ASM_OPERANDS)
- abort ();
-
- if (GET_CODE (pat) != SET)
- return;
-
- dest = * get_true_reg (& SET_DEST (pat));
-
- /* The destination is dead before this insn. If the destination is
- not used after this insn, record this with REG_UNUSED. */
+ register char *fmt;
+ register int i;
- if (STACK_REG_P (dest))
+ if (STACK_REG_P (pat))
{
- /* ??? This check is unnecessary. */
-
- if (find_regno_note (insn, REG_UNUSED, REGNO (dest)))
- abort ();
+ if (src)
+ SET_HARD_REG_BIT (*src, REGNO (pat));
- if (! TEST_HARD_REG_BIT (regstack->reg_set, REGNO (dest)))
- REG_NOTES (insn) = gen_rtx (EXPR_LIST, REG_UNUSED, dest,
- REG_NOTES (insn));
+ if (dest)
+ SET_HARD_REG_BIT (*dest, REGNO (pat));
- CLEAR_HARD_REG_BIT (regstack->reg_set, REGNO (dest));
+ return;
}
- else
- if (dest != cc0_rtx && stack_regs_mentioned_p (dest))
- abort ();
-
- src = * get_true_reg (& SET_SRC (pat));
- switch (GET_CODE (src))
+ if (GET_CODE (pat) == SET)
{
- /* ??? get_true_reg will make some of these cases redundant. */
-
- case PLUS:
- case MINUS:
- case MULT:
- case DIV:
- case COMPARE:
- record_note_if_dead (insn, regstack, XEXP (src, 0), dest);
- record_note_if_dead (insn, regstack, XEXP (src, 1), dest);
- break;
-
- case ABS:
- case NEG:
- case SQRT:
- case FLOAT_EXTEND:
- case FLOAT_TRUNCATE:
- case FLOAT:
- case UNSIGNED_FLOAT:
- record_note_if_dead (insn, regstack, XEXP (src, 0), dest);
- break;
-
- case UNSIGNED_FIX:
- case FIX:
- src = XEXP (src, 0);
- if (GET_CODE (src) == FIX)
- record_note_if_dead (insn, regstack, XEXP (src, 0), dest);
- else
- record_note_if_dead (insn, regstack, src, dest);
- break;
-
- case ASM_OPERANDS:
- case ASM_INPUT:
- abort (); /* we should have caught this already. */
- break;
+ record_reg_life_pat (XEXP (pat, 0), NULL_PTR, dest);
+ record_reg_life_pat (XEXP (pat, 1), src, NULL_PTR);
+ return;
+ }
- case REG:
- record_note_if_dead (insn, regstack, src, dest);
- break;
+ /* We don't need to consider either of these cases. */
+ if (GET_CODE (pat) == USE || GET_CODE (pat) == CLOBBER)
+ return;
- default:
- /* If a stack register appears in the src RTL, it is a bug, and
- code should be added above to handle it. */
+ 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;
- if (stack_regs_mentioned_p (src))
- abort ();
+ for (j = XVECLEN (pat, i) - 1; j >= 0; j--)
+ record_reg_life_pat (XVECEXP (pat, i, j), src, dest);
+ }
+ else if (fmt[i] == 'e')
+ record_reg_life_pat (XEXP (pat, i), src, dest);
}
}
\f
return;
}
- if (GET_CODE (PATTERN (insn)) == PARALLEL)
+ /* An insn referencing a stack reg has a mode of QImode. */
+ if (GET_MODE (insn) == QImode)
{
- register int i;
+ HARD_REG_SET src, dest;
+ int regno;
+
+ CLEAR_HARD_REG_SET (src);
+ CLEAR_HARD_REG_SET (dest);
+ record_reg_life_pat (PATTERN (insn), &src, &dest);
+
+ 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][(int) 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][(int) DFmode],
+ REG_NOTES (insn));
+ }
- for (i = 0; i < XVECLEN (PATTERN (insn), 0); i++)
- record_reg_life_pat (insn, regstack, XVECEXP (PATTERN (insn), 0, i));
+ AND_COMPL_HARD_REG_SET (regstack->reg_set, dest);
+ IOR_HARD_REG_SET (regstack->reg_set, src);
}
- else if (GET_MODE (insn) == QImode)
- record_reg_life_pat (insn, regstack, PATTERN (insn));
/* There might be a reg that is live after a function call.
Initialize it to zero so that the program does not crash. See comment
int reg = FIRST_FLOAT_REG;
/* If a stack reg is mentioned in a CALL_INSN, it must be as the
- return value; conversely, if a float is returned, a stack reg
- must be mentioned. */
+ return value. */
if (stack_regs_mentioned_p (PATTERN (insn)))
reg++;
register int block;
register RTX_CODE prev_code = BARRIER;
register RTX_CODE code;
+ rtx label_value_list = 0;
/* Record where all the blocks start and end.
Record which basic blocks control can drop in to. */
for (insn = first; insn; insn = NEXT_INSN (insn))
{
/* Note that this loop must select the same block boundaries
- as code in reg_to_stack. */
+ as code in reg_to_stack, but that these are not the same
+ as those selected in flow.c. */
code = GET_CODE (insn);
|| (prev_code != INSN
&& prev_code != CALL_INSN
&& prev_code != CODE_LABEL
- && (code == INSN || code == CALL_INSN || code == JUMP_INSN)))
+ && GET_RTX_CLASS (code) == 'i'))
{
block_begin[++block] = insn;
block_end[block] = insn;
block_drops_in[block] = prev_code != BARRIER;
}
- else if (code == INSN || code == CALL_INSN || code == JUMP_INSN)
+ else if (GET_RTX_CLASS (code) == 'i')
block_end[block] = insn;
- BLOCK_NUM (insn) = block;
+ if (GET_RTX_CLASS (code) == 'i')
+ {
+ rtx note;
+
+ /* Make a list of all labels referred to other than by jumps. */
+ 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);
+ }
- if (code == CODE_LABEL)
- LABEL_REFS (insn) = insn; /* delete old chain */
+ BLOCK_NUM (insn) = block;
if (code != NOTE)
prev_code = code;
insn = block_end[block];
if (GET_CODE (insn) == JUMP_INSN)
- record_label_references (insn, PATTERN (insn));
+ {
+ rtx pat = PATTERN (insn);
+ int computed_jump = 0;
+ rtx x;
+
+ if (GET_CODE (pat) == PARALLEL)
+ {
+ int len = XVECLEN (pat, 0);
+ int has_use_labelref = 0;
+ int i;
+
+ for (i = len - 1; i >= 0; i--)
+ if (GET_CODE (XVECEXP (pat, 0, i)) == USE
+ && GET_CODE (XEXP (XVECEXP (pat, 0, i), 0)) == LABEL_REF)
+ has_use_labelref = 1;
+
+ if (! has_use_labelref)
+ for (i = len - 1; i >= 0; i--)
+ if (GET_CODE (XVECEXP (pat, 0, i)) == SET
+ && SET_DEST (XVECEXP (pat, 0, i)) == pc_rtx
+ && uses_reg_or_mem (SET_SRC (XVECEXP (pat, 0, i))))
+ computed_jump = 1;
+ }
+ else if (GET_CODE (pat) == SET
+ && SET_DEST (pat) == pc_rtx
+ && uses_reg_or_mem (SET_SRC (pat)))
+ computed_jump = 1;
+
+ if (computed_jump)
+ {
+ 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);
+ }
+ }
+}
+
+/* Return 1 if X contain a REG or MEM that is not in the constant pool. */
+
+static int
+uses_reg_or_mem (x)
+ rtx x;
+{
+ enum rtx_code code = GET_CODE (x);
+ int i, j;
+ char *fmt;
+
+ if (code == REG
+ || (code == MEM
+ && ! (GET_CODE (XEXP (x, 0)) == SYMBOL_REF
+ && CONSTANT_POOL_ADDRESS_P (XEXP (x, 0)))))
+ return 1;
+
+ fmt = GET_RTX_FORMAT (code);
+ for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
+ {
+ if (fmt[i] == 'e'
+ && uses_reg_or_mem (XEXP (x, i)))
+ return 1;
+
+ if (fmt[i] == 'E')
+ for (j = 0; j < XVECLEN (x, i); j++)
+ if (uses_reg_or_mem (XVECEXP (x, i, j)))
+ return 1;
+ }
+
+ return 0;
+}
+
+/* If current function returns its result in an fp stack register,
+ return the register number. Otherwise return -1. */
+
+static int
+stack_result_p (decl)
+ tree decl;
+{
+ rtx 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 STACK_REG_P (result) ? REGNO (result) : -1;
}
\f
/* Determine the which registers are live at the start of each basic
int reg, block;
struct stack_def regstack;
- if (current_function_returns_real)
+ if (current_function_returns_real
+ && stack_result_p (current_function_decl) >= 0)
{
/* Find all RETURN insns and mark them. */
+ int value_regno = stack_result_p (current_function_decl);
+
for (block = blocks - 1; block >= 0; block--)
if (GET_CODE (block_end[block]) == JUMP_INSN
&& GET_CODE (PATTERN (block_end[block])) == RETURN)
- SET_HARD_REG_BIT (block_out_reg_set[block], FIRST_STACK_REG);
+ SET_HARD_REG_BIT (block_out_reg_set[block], value_regno);
/* Mark of the end of last block if we "fall off" the end of the
function into the epilogue. */
if (GET_CODE (block_end[blocks-1]) != JUMP_INSN
|| GET_CODE (PATTERN (block_end[blocks-1])) == RETURN)
- SET_HARD_REG_BIT (block_out_reg_set[blocks-1], FIRST_STACK_REG);
+ SET_HARD_REG_BIT (block_out_reg_set[blocks-1], value_regno);
}
/* now scan all blocks backward for stack register use */
FP_mode_reg[FIRST_STACK_REG][(int) DFmode]);
pop_insn = (*when) (pop_rtx, insn);
- PUT_MODE (pop_insn, VOIDmode);
+ /* ??? This used to be VOIDmode, but that seems wrong. */
+ PUT_MODE (pop_insn, QImode);
REG_NOTES (pop_insn) = gen_rtx (EXPR_LIST, REG_DEAD,
FP_mode_reg[FIRST_STACK_REG][(int) DFmode],
If REG is already at the top of the stack, no insn is emitted. */
static void
-emit_hard_swap_insn (insn, regstack, hard_regno, when)
+emit_swap_insn (insn, regstack, reg)
rtx insn;
stack regstack;
- int hard_regno;
- rtx (*when)();
+ rtx reg;
{
+ int hard_regno;
rtx gen_swapdf();
rtx swap_rtx, swap_insn;
- int tmp, other;
+ int tmp, other_reg; /* swap regno temps */
+ rtx i1; /* the stack-reg insn prior to INSN */
+ rtx i1set = NULL_RTX; /* the SET rtx within I1 */
+
+ hard_regno = get_hard_regnum (regstack, reg);
+ if (hard_regno < FIRST_STACK_REG)
+ abort ();
if (hard_regno == FIRST_STACK_REG)
return;
- swap_rtx = gen_swapdf (FP_mode_reg[hard_regno][(int) DFmode],
- FP_mode_reg[FIRST_STACK_REG][(int) DFmode]);
- swap_insn = (*when) (swap_rtx, insn);
- PUT_MODE (swap_insn, VOIDmode);
-
- other = regstack->top - (hard_regno - FIRST_STACK_REG);
+ other_reg = regstack->top - (hard_regno - FIRST_STACK_REG);
- tmp = regstack->reg[other];
- regstack->reg[other] = regstack->reg[regstack->top];
+ tmp = regstack->reg[other_reg];
+ regstack->reg[other_reg] = regstack->reg[regstack->top];
regstack->reg[regstack->top] = tmp;
-}
-/* Emit an insn before or after INSN to swap virtual register REG with the
- top of stack. See comments before emit_hard_swap_insn. */
+ /* Find the previous insn involving stack regs, but don't go past
+ any labels, calls or jumps. */
+ i1 = prev_nonnote_insn (insn);
+ while (i1 && GET_CODE (i1) == INSN && GET_MODE (i1) != QImode)
+ i1 = prev_nonnote_insn (i1);
-static void
-emit_swap_insn (insn, regstack, reg, when)
- rtx insn;
- stack regstack;
- rtx reg;
- rtx (*when)();
-{
- int hard_regno;
+ if (i1)
+ i1set = single_set (i1);
- hard_regno = get_hard_regnum (regstack, reg);
- if (hard_regno < FIRST_STACK_REG)
- abort ();
+ if (i1set)
+ {
+ rtx i2; /* the stack-reg insn prior to I1 */
+ rtx i1src = *get_true_reg (&SET_SRC (i1set));
+ rtx i1dest = *get_true_reg (&SET_DEST (i1set));
+
+ /* If the previous register stack push was from the reg we are to
+ swap with, omit the swap. */
+
+ 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 the previous insn wrote to the reg we are to swap with,
+ omit the swap. */
+
+ 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 ();
+ }
- emit_hard_swap_insn (insn, regstack, hard_regno, when);
+ swap_rtx = gen_swapdf (FP_mode_reg[hard_regno][(int) DFmode],
+ FP_mode_reg[FIRST_STACK_REG][(int) DFmode]);
+ swap_insn = emit_insn_after (swap_rtx, i1);
+ /* ??? This used to be VOIDmode, but that seems wrong. */
+ PUT_MODE (swap_insn, QImode);
}
\f
/* Handle a move to or from a stack register in PAT, which is in INSN.
only top of stack may be saved, emit an exchange first if
needs be. */
- emit_swap_insn (insn, regstack, *src, emit_insn_before);
+ emit_swap_insn (insn, regstack, *src);
note = find_regno_note (insn, REG_DEAD, REGNO (*src));
if (note)
regstack->top--;
CLEAR_HARD_REG_BIT (regstack->reg_set, REGNO (*src));
}
+ else if (GET_MODE (*src) == XFmode && regstack->top != REG_STACK_SIZE)
+ {
+ /* A 387 cannot write an XFmode value to a MEM without
+ clobbering the source reg. The output code can handle
+ this by reading back the value from the MEM.
+ But it is more efficient to use a temp register if one is
+ available. Push the source value here if the register
+ stack is not full, and then write the value to memory via
+ a pop. */
+ rtx push_rtx, push_insn;
+ rtx top_stack_reg = FP_mode_reg[FIRST_STACK_REG][(int) XFmode];
+
+ push_rtx = gen_movxf (top_stack_reg, top_stack_reg);
+ push_insn = emit_insn_before (push_rtx, insn);
+ PUT_MODE (push_insn, QImode);
+ REG_NOTES (insn) = gen_rtx (EXPR_LIST, REG_DEAD, top_stack_reg,
+ REG_NOTES (insn));
+ }
replace_reg (src, FIRST_STACK_REG);
}
abort ();
}
\f
+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));
+ }
+}
+
/* Handle a comparison. Special care needs to be taken to avoid
causing comparisons that a 387 cannot do correctly, such as EQ.
src1 = get_true_reg (&XEXP (SET_SRC (pat), 0));
src2 = get_true_reg (&XEXP (SET_SRC (pat), 1));
- /* The first argument must always be a stack reg. */
- /* ??? why? */
+ /* ??? If fxch turns out to be cheaper than fstp, give priority to
+ registers that die in this insn - move those to stack top first. */
+ 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;
- if (! STACK_REG_P (*src1))
- abort ();
+ 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;
+ }
/* We will fix any death note later. */
if (STACK_REG_P (*src2))
src2_note = find_regno_note (insn, REG_DEAD, REGNO (*src2));
else
- src2_note = 0;
+ src2_note = NULL_RTX;
- emit_swap_insn (insn, regstack, *src1, emit_insn_before);
+ emit_swap_insn (insn, regstack, *src1);
replace_reg (src1, FIRST_STACK_REG);
needed, and it was just handled. */
if (src2_note
- && ! (STACK_REG_P (*src1)
- && STACK_REG_P (*src2)
+ && ! (STACK_REG_P (*src1) && STACK_REG_P (*src2)
&& REGNO (*src1) == REGNO (*src2)))
{
/* As a special case, two regs may die in this insn if src2 is
rtx pat;
{
rtx *dest, *src;
- rtx *src1 = 0, *src2;
+ rtx *src1 = (rtx *) NULL_PTR, *src2;
rtx src1_note, src2_note;
if (GET_CODE (pat) != SET)
/* Fall through. */
+ 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, emit_insn_before);
+ emit_swap_insn (insn, regstack, *src1);
src1_note = find_regno_note (insn, REG_DEAD, REGNO (*src1));
if (STACK_REG_P (*src1))
src1_note = find_regno_note (insn, REG_DEAD, REGNO (*src1));
else
- src1_note = 0;
+ src1_note = NULL_RTX;
if (STACK_REG_P (*src2))
src2_note = find_regno_note (insn, REG_DEAD, REGNO (*src2));
else
- src2_note = 0;
+ src2_note = NULL_RTX;
/* If either operand is not a stack register, then the dest
must be top of stack. */
if (! STACK_REG_P (*src1) || ! STACK_REG_P (*src2))
- emit_swap_insn (insn, regstack, *dest, emit_insn_before);
+ emit_swap_insn (insn, regstack, *dest);
else
{
/* Both operands are REG. If neither operand is already
at the top of stack, choose to make the one that is the dest
- the new top of stack.
-
- ??? A later optimization here would be to look forward
- in the insns and see which source reg will be needed at top
- of stack soonest. */
+ the new top of stack. */
int src1_hard_regnum, src2_hard_regnum;
if (src1_hard_regnum != FIRST_STACK_REG
&& src2_hard_regnum != FIRST_STACK_REG)
- emit_swap_insn (insn, regstack, *dest, emit_insn_before);
+ emit_swap_insn (insn, regstack, *dest);
}
if (STACK_REG_P (*src1))
break;
+ case UNSPEC:
+ switch (XINT (SET_SRC (pat), 1))
+ {
+ case 1: /* sin */
+ case 2: /* cos */
+ /* These insns only operate on the top of the stack. */
+
+ 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;
+
default:
abort ();
}
abort ();
emit_swap_insn (insn, old,
- FP_mode_reg[old->reg[reg]][(int) DFmode],
- emit_insn_before);
+ FP_mode_reg[old->reg[reg]][(int) DFmode]);
}
/* See if any regs remain incorrect. If so, bring an
if (new->reg[reg] != old->reg[reg])
{
emit_swap_insn (insn, old,
- FP_mode_reg[old->reg[reg]][(int) DFmode],
- emit_insn_before);
+ FP_mode_reg[old->reg[reg]][(int) DFmode]);
break;
}
} while (reg >= 0);
abort ();
/* First, see if in fact anything needs to be done to the stack at all. */
+ if (INSN_UID (label) <= 0)
+ return;
label_stack = &block_stack_in[BLOCK_NUM (label)];