summaryrefslogtreecommitdiff
path: root/gcc/reg-stack.c
diff options
context:
space:
mode:
authorjrv <jrv@138bc75d-0d04-0410-961f-82ee72b054a4>1991-12-26 10:05:42 +0000
committerjrv <jrv@138bc75d-0d04-0410-961f-82ee72b054a4>1991-12-26 10:05:42 +0000
commit535825e69a252f305fbdbb98a0a928ad06d52f02 (patch)
tree9d830b14dc0b1dab3fb2577a9f05241d7621865b /gcc/reg-stack.c
parent1acadd8b5f99fe0afb05912c2226d461cd35158e (diff)
downloadgcc-535825e69a252f305fbdbb98a0a928ad06d52f02.tar.gz
Initial revision
git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@142 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'gcc/reg-stack.c')
-rw-r--r--gcc/reg-stack.c1938
1 files changed, 1938 insertions, 0 deletions
diff --git a/gcc/reg-stack.c b/gcc/reg-stack.c
new file mode 100644
index 00000000000..f69fd9f5ab0
--- /dev/null
+++ b/gcc/reg-stack.c
@@ -0,0 +1,1938 @@
+/* Register to Stack convert for GNU compiler.
+ Copyright (C) 1990 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, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* This pass converts stack-like registers from the "flat register
+ file" model that gcc uses, to a stack convention that the 387 uses.
+
+ * The form of the input:
+
+ On input, the function consists of insn that have had their
+ registers fully allocated to a set of "virtual" registers. Note that
+ the word "virtual" is used differently here than elsewhere in gcc: for
+ each virtual stack reg, there is a hard reg, but the mapping between
+ them is not known until this pass is run. On output, hard register
+ numbers have been substituted, and various pop and exchange insns have
+ been emitted. The hard register numbers and the virtual register
+ numbers completely overlap - before this pass, all stack register
+ numbers are virtual, and afterward they are all hard, with the
+ exception of ASM_OPERANDS, which are discussed below.
+
+ The virtual registers can be manipulated normally by gcc, and their
+ semantics are the same as for normal registers. After the hard
+ register numbers are substituted, the semantics of an insn containing
+ stack-like regs are not the same as for an insn with normal regs: for
+ instance, it is not safe to delete an insn that appears to be a no-op
+ move. In general, no insn containing hard regs should be changed
+ after this pass is done.
+
+ * The form of the output:
+
+ After this pass, hard register numbers represent the distance from
+ the current top of stack to the desired register. A reference to
+ FIRST_STACK_REG references the top of stack, FIRST_STACK_REG + 1,
+ represents the register just below that, and so forth. Also, REG_DEAD
+ notes indicate whether or not a stack register should be popped.
+
+ A "swap" insn looks like a parallel of two patterns, where each
+ pattern is a SET: one sets A to B, the other B to A.
+
+ A "push" or "load" insn is a SET whose SET_DEST is FIRST_STACK_REG
+ and whose SET_DEST is REG or MEM. Any other SET_DEST, such as PLUS,
+ will replace the existing stack top, not push a new value.
+
+ A store insn is a SET whose SET_DEST is FIRST_STACK_REG, and whose
+ SET_SRC is REG or MEM.
+
+ The case where both the SET_SRC and SET_DEST FIRST_STACK_REG
+ appears ambiguous. As a special case, the presence of a REG_DEAD note
+ for FIRST_STACK_REG differentiates between a load insn and a pop.
+
+ If a REG_DEAD is present, the insn represents a "pop" that discards
+ the top of the register stack. If there is no REG_DEAD note, then the
+ insn represents a "dup" or a push of the current top of stack onto the
+ stack.
+
+ * Methodology:
+
+ Existing REG_DEAD and REG_UNUSED notes for stack registers are
+ deleted and recreated from scratch. REG_DEAD is never created for a
+ SET_DEST, only REG_UNUSED.
+
+ Before life analysis, the mode of each insn is set based on whether
+ or not any stack registers are mentioned within that insn. VOIDmode
+ means that no regs are mentioned anyway, and QImode means that at
+ least one pattern within the insn mentions stack registers. This
+ information is valid until after reg_to_stack returns, and is used
+ from jump_optimize.
+
+ * Limitations:
+
+ Inline assembly isn't handled yet. */
+
+#include <stdio.h>
+#include "config.h"
+#include "tree.h"
+#include "rtl.h"
+#include "regs.h"
+#include "hard-reg-set.h"
+#include "flags.h"
+
+#ifdef STACK_REGS
+
+#define REG_STACK_SIZE (LAST_STACK_REG - FIRST_STACK_REG + 1)
+
+/* True if the current function returns a real value. */
+static int current_function_returns_real;
+
+/* 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. */
+
+typedef struct stack_def
+{
+ int top; /* index to top stack element */
+ HARD_REG_SET reg_set; /* set of live registers */
+ char reg[REG_STACK_SIZE]; /* register - stack mapping */
+} *stack;
+
+/* highest instruction uid */
+static int max_uid = 0;
+
+/* Number of basic blocks in the current function. */
+static int blocks;
+
+/* Element N is first insn in basic block N.
+ This info lasts until we finish compiling the function. */
+static rtx *block_begin;
+
+/* Element N is last insn in basic block N.
+ This info lasts until we finish compiling the function. */
+static rtx *block_end;
+
+/* Element N is nonzero if control can drop into basic block N */
+static char *block_drops_in;
+
+/* Element N says all about the stack at entry block N */
+static stack block_stack_in;
+
+/* Element N says all about the stack life at the end of block N */
+static HARD_REG_SET *block_out_reg_set;
+
+/* This is where the BLOCK_NUM values are really stored. This is set
+ up by find_blocks and used there and in life_analysis. It can be used
+ 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;
+
+/* This is the register file for all register after conversion */
+static rtx SFmode_reg[FIRST_PSEUDO_REGISTER];
+static rtx DFmode_reg[FIRST_PSEUDO_REGISTER];
+
+/* ??? set of register to delete after ASM_OPERAND */
+HARD_REG_SET asm_regs;
+
+/* Get the basic block number of an insn. See note at block_number
+ definition are validity of this information. */
+
+#define BLOCK_NUM(INSN) \
+ (((INSN_UID (INSN) > max_uid) \
+ ? (short *)(abort() , 0) \
+ : block_number)[INSN_UID (INSN)])
+
+extern rtx gen_jump ();
+extern rtx gen_movdf ();
+extern rtx find_regno_note ();
+extern rtx emit_jump_insn_before ();
+extern rtx emit_label_after ();
+
+extern rtx dconst0_rtx;
+
+/* Forward declarations */
+
+static void find_blocks ();
+static void stack_reg_life_analysis ();
+static void convert_regs ();
+static void dump_stack_info ();
+static void fatal_for_asm ();
+
+
+/* Return non-zero if any stack register is mentioned somewhere within
+ PAT. */
+
+static int
+stack_regs_mentioned_p (pat)
+ register 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;
+}
+
+/* Convert register usage from "flat" register file usage to a "stack
+ register file. FIRST is the first insn in the function, FILE is the
+ dump file, if used.
+
+ First compute the beginning and end of each basic block. Do a
+ register life analysis on the stack registers, recording the result
+ for the head and tail of each basic block. The convert each insn one
+ by one. Run a last jump_optimize() pass, if optimizing, to eliminate
+ any cross-jumping created when the converter inserts pop insns.*/
+
+void
+reg_to_stack (first, file)
+ rtx first;
+ FILE *file;
+{
+ register rtx insn;
+ register int i;
+ int stack_reg_seen = 0;
+
+ current_function_returns_real
+ = TREE_CODE (TREE_TYPE (DECL_RESULT (current_function_decl))) == REAL_TYPE;
+
+ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+ {
+ SFmode_reg[i] = gen_rtx (REG, SFmode, i);
+ DFmode_reg[i] = gen_rtx (REG, DFmode, i);
+ }
+
+ /* Count the basic blocks. Also find maximum insn uid. */
+ {
+ register RTX_CODE prev_code = JUMP_INSN;
+ register RTX_CODE code;
+
+ max_uid = 0;
+ blocks = 0;
+ for (insn = first; insn; insn = NEXT_INSN (insn))
+ {
+ /* Note that this loop must select the same block boundaries
+ as code in find_blocks. */
+
+ 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
+ && (code == INSN || code == CALL_INSN || code == JUMP_INSN)))
+ 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)
+ && stack_regs_mentioned_p (PATTERN (insn)))
+ {
+ stack_reg_seen = 1;
+ PUT_MODE (insn, QImode);
+ }
+ else
+ PUT_MODE (insn, VOIDmode);
+
+ if (code != NOTE)
+ prev_code = code;
+ }
+ }
+
+ /* If no stack register reference exists in this insn, there isn't
+ anything to convert. */
+
+ if (! stack_reg_seen)
+ return;
+
+ /* If there are stack registers, there must be at least one block. */
+
+ if (! blocks)
+ abort ();
+
+ /* Allocate some tables that last till end of compiling this function
+ and some needed only in find_blocks and life_analysis. */
+
+ 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 (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));
+
+ find_blocks (first);
+ stack_reg_life_analysis (first);
+
+ /* Dump the life analysis debug information before jump
+ optimization, as that will destroy the LABEL_REFS we keep the
+ information in. */
+
+ if (file)
+ dump_stack_info (file);
+
+ convert_regs ();
+
+ if (optimize)
+ jump_optimize (first, 2, 0, 0);
+}
+
+/* Check PAT, which is in INSN, for LABEL_REFs. Add INSN to the
+ label's chain of references, and note which insn contains each
+ reference. */
+
+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 ();
+
+ /* Don't make a duplicate in the code_label's chain. */
+
+ for (ref = LABEL_REFS (label); 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));
+ }
+ }
+}
+
+/* Return a pointer to the REG expression within PAT. If PAT is not a
+ REG, possible enclosed by a conversion rtx, return the inner part of
+ PAT that stopped the search. */
+
+static rtx *
+get_true_reg (pat)
+ rtx *pat;
+{
+ while (GET_CODE (*pat) == SUBREG
+ || GET_CODE (*pat) == FLOAT
+ || GET_CODE (*pat) == FIX
+ || GET_CODE (*pat) == FLOAT_EXTEND
+ || GET_CODE (*pat) == FLOAT_TRUNCATE)
+ 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 ();
+}
+
+/* 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.
+
+ 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;
+ rtx pat;
+{
+ rtx src, dest;
+
+ if (GET_CODE (pat) == CLOBBER
+ && GET_CODE (PATTERN (insn)) == PARALLEL
+ && GET_CODE (SET_SRC (XVECEXP (PATTERN (insn), 0, 0))) == ASM_OPERANDS)
+ {
+ if (STACK_REG_P (XEXP (pat, 0)))
+ abort ();
+ return;
+ }
+
+ 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. */
+
+ if (STACK_REG_P (dest))
+ {
+ /* ??? This check is unnecessary. */
+
+ if (find_regno_note (insn, REG_UNUSED, REGNO (dest)))
+ abort ();
+
+ if (! TEST_HARD_REG_BIT (regstack->reg_set, REGNO (dest)))
+ REG_NOTES (insn) = gen_rtx (EXPR_LIST, REG_UNUSED, dest,
+ REG_NOTES (insn));
+
+ CLEAR_HARD_REG_BIT (regstack->reg_set, REGNO (dest));
+ }
+ else
+ if (dest != cc0_rtx && stack_regs_mentioned_p (dest))
+ abort ();
+
+ src = * get_true_reg (& SET_SRC (pat));
+
+ switch (GET_CODE (src))
+ {
+ /* ??? 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:
+ {
+ register int j;
+
+ /* ??? This needs much improvement */
+
+ if (stack_regs_mentioned_p (pat))
+ abort ();
+
+ for (j = 0; j < XVECLEN (src, 3); j++)
+ record_note_if_dead (insn, regstack, XVECEXP (src, 3, j), dest);
+ }
+ break;
+
+ case REG:
+ record_note_if_dead (insn, regstack, src, dest);
+ break;
+
+ default:
+ /* If a stack register appears in the src RTL, it is a bug, and
+ code should be added above to handle it. */
+
+ if (stack_regs_mentioned_p (src))
+ abort ();
+ }
+}
+
+/* Scan INSN, which is in BLOCK, and record the life & death of stack
+ registers in REGSTACK. This function is called to process insns from
+ the last insn in a block to the first. The actual scanning is done in
+ record_reg_life_pat.
+
+ If a register is live after a CALL_INSN, but is not a value return
+ register for that CALL_INSN, then code is emitted to initialize that
+ register. The block_end[] data is kept accurate.
+
+ Existing death and unset notes for stack registers are deleted
+ before processing the insn. */
+
+static void
+record_reg_life (insn, block, regstack)
+ rtx insn;
+ int block;
+ stack regstack;
+{
+ rtx note, *note_link;
+
+ if ((GET_CODE (insn) != INSN && GET_CODE (insn) != CALL_INSN)
+ || INSN_DELETED_P (insn))
+ return;
+
+ /* Strip death notes for stack regs from this insn */
+
+ note_link = &REG_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);
+
+ /* Process all patterns in the insn. */
+
+ if (GET_CODE (PATTERN (insn)) == PARALLEL)
+ {
+ register int i;
+
+ for (i = 0; i < XVECLEN (PATTERN (insn), 0); i++)
+ record_reg_life_pat (insn, regstack, XVECEXP (PATTERN (insn), 0, i));
+ }
+ 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
+ towards the end of stack_reg_life_analysis(). */
+
+ if (GET_CODE (insn) == CALL_INSN)
+ {
+ 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. */
+
+ if (stack_regs_mentioned_p (PATTERN (insn)))
+ reg++;
+
+ for (; reg <= LAST_STACK_REG; reg++)
+ if (TEST_HARD_REG_BIT (regstack->reg_set, reg))
+ {
+ rtx init, pat;
+
+ /* The insn will use virtual register numbers, and so
+ convert_regs is expected to process these. But BLOCK_NUM
+ cannot be used on these insns, because they do not appear in
+ block_number[]. */
+
+ pat = gen_rtx (SET, VOIDmode, DFmode_reg[reg], dconst0_rtx);
+ init = emit_insn_after (pat, insn);
+ PUT_MODE (init, QImode);
+
+ CLEAR_HARD_REG_BIT (regstack->reg_set, reg);
+
+ /* If the CALL_INSN was the end of a block, move the
+ block_end to point to the new insn. */
+
+ if (block_end[block] == insn)
+ block_end[block] = init;
+ }
+
+ /* Some regs do not survive a CALL */
+
+ AND_COMPL_HARD_REG_SET (regstack->reg_set, call_used_reg_set);
+ }
+}
+
+/* Find all basic blocks of the function, which starts with FIRST.
+ For each JUMP_INSN, build the chain of LABEL_REFS on each CODE_LABEL. */
+
+static void
+find_blocks (first)
+ rtx first;
+{
+ register rtx insn;
+ register int block;
+ register RTX_CODE prev_code = BARRIER;
+ register RTX_CODE code;
+
+ /* Record where all the blocks start and end.
+ Record which basic blocks control can drop in to. */
+
+ block = -1;
+ for (insn = first; insn; insn = NEXT_INSN (insn))
+ {
+ /* Note that this loop must select the same block boundaries
+ as code in reg_to_stack. */
+
+ code = GET_CODE (insn);
+
+ if (code == CODE_LABEL
+ || (prev_code != INSN
+ && prev_code != CALL_INSN
+ && prev_code != CODE_LABEL
+ && (code == INSN || code == CALL_INSN || code == JUMP_INSN)))
+ {
+ 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)
+ block_end[block] = insn;
+
+ BLOCK_NUM (insn) = block;
+
+ if (code == CODE_LABEL)
+ LABEL_REFS (insn) = insn; /* delete old chain */
+
+ if (code != NOTE)
+ prev_code = code;
+ }
+
+ if (block + 1 != blocks)
+ abort ();
+
+ /* generate all label references to the correspondending jump insn */
+ for (block = 0; block < blocks; block++)
+ {
+ insn = block_end[block];
+
+ if (GET_CODE (insn) == JUMP_INSN)
+ record_label_references (insn, PATTERN (insn));
+ }
+}
+
+/* Determine the which registers are live at the start of each basic
+ block of the function whose first insn is FIRST.
+
+ First, if the function returns a real_type, mark the function
+ return type as live at each return point, as the RTL may not give any
+ hint that the register is live.
+
+ Then, start with the last block and work back to the first block.
+ Similarly, work backwards within each block, insn by insn, recording
+ which regs are die and which are used (and therefore live) in the
+ hard reg set of block_stack_in[].
+
+ After processing each basic block, if there is a label at the start
+ of the block, propagate the live registers to all jumps to this block.
+
+ As a special case, if there are regs live in this block, that are
+ not live in a block containing a jump to this label, and the block
+ containing the jump has already been processed, we must propagate this
+ block's entry register life back to the block containing the jump, and
+ restart life analysis from there.
+
+ In the worst case, this function may traverse the insns
+ REG_STACK_SIZE times. This is necessary, since a jump towards the end
+ of the insns may not know that a reg is live at a target that is early
+ in the insns. So we back up and start over with the new reg live.
+
+ If there are registers that are live at the start of the function,
+ insns are emitted to initialize these registers. Something similar is
+ done after CALL_INSNs in record_reg_life. */
+
+static void
+stack_reg_life_analysis (first)
+ rtx first;
+{
+ int reg, block;
+ struct stack_def regstack;
+
+ if (current_function_returns_real)
+ {
+ /* Find all RETURN insns and mark them. */
+
+ 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);
+
+ /* 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);
+ }
+
+ /* now scan all blocks backward for stack register use */
+
+ block = blocks - 1;
+ while (block >= 0)
+ {
+ register rtx insn, prev;
+
+ /* current register status at last instruction */
+
+ COPY_HARD_REG_SET (regstack.reg_set, block_out_reg_set[block]);
+
+ prev = block_end[block];
+ do
+ {
+ insn = prev;
+ prev = PREV_INSN (insn);
+
+ /* If the insn is a CALL_INSN, we need to ensure that
+ everything dies. But otherwise don't process unless there
+ are some stack regs present. */
+
+ if (GET_MODE (insn) == QImode || GET_CODE (insn) == CALL_INSN)
+ record_reg_life (insn, block, &regstack);
+
+ } while (insn != block_begin[block]);
+
+ /* Set the state at the start of the block. Mark that no
+ register mapping information known yet. */
+
+ COPY_HARD_REG_SET (block_stack_in[block].reg_set, regstack.reg_set);
+ block_stack_in[block].top = -2;
+
+ /* If there is a label, propagate our register life to all jumps
+ to this label. */
+
+ 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
+ {
+ /* The block containing the jump has already been
+ processed. If there are registers that were not known
+ to be live then, but are live now, we must back up
+ and restart life analysis from that point with the new
+ life information. */
+
+ 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;
+
+ 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;
+ }
+
+ {
+ /* If any reg is live at the start of the first block of a
+ function, then we must guarantee that the reg holds some value by
+ generating our own "load" of that register. Otherwise a 387 would
+ fault trying to access an empty register. */
+
+ HARD_REG_SET empty_regs;
+ CLEAR_HARD_REG_SET (empty_regs);
+ GO_IF_HARD_REG_SUBSET (block_stack_in[0].reg_set, empty_regs,
+ no_live_regs);
+ }
+
+ /* Load zero into each live register. The fact that a register
+ appears live at the function start does not necessarily imply an error
+ in the user program: it merely means that we could not determine that
+ there wasn't such an error, just as -Wunused sometimes gives
+ "incorrect" warnings. In those cases, these initializations will do
+ no harm.
+
+ Note that we are inserting virtual register references here:
+ these insns must be processed by convert_regs later. Also, these
+ insns will not be in block_number, so BLOCK_NUM() will fail for them. */
+
+ for (reg = LAST_STACK_REG; reg >= FIRST_STACK_REG; reg--)
+ if (TEST_HARD_REG_BIT (block_stack_in[0].reg_set, reg))
+ {
+ rtx init_rtx;
+
+ init_rtx = gen_rtx (SET, VOIDmode, DFmode_reg[reg], dconst0_rtx);
+ block_begin[0] = emit_insn_after (init_rtx, first);
+ PUT_MODE (block_begin[0], QImode);
+
+ CLEAR_HARD_REG_BIT (block_stack_in[0].reg_set, reg);
+ }
+
+ no_live_regs:
+ ;
+}
+
+/*****************************************************************************
+ This section deals with stack register substition, and forms the second
+ pass over the RTL.
+ *****************************************************************************/
+
+/* Replace REG, which is a pointer to a stack reg RTX, with an RTX for
+ the desired hard REGNO. */
+
+static void
+replace_reg (reg, regno)
+ rtx *reg;
+ int regno;
+{
+ if (regno < FIRST_STACK_REG || regno > LAST_STACK_REG
+ || ! STACK_REG_P (*reg))
+ abort ();
+
+ if (GET_MODE (*reg) == DFmode)
+ *reg = DFmode_reg[regno];
+ else if (GET_MODE (*reg) == SFmode)
+ *reg = SFmode_reg[regno];
+ else
+ abort ();
+}
+
+/* Remove a note of type NOTE, which must be found, for register
+ number REGNO from INSN. Remove only one such note. */
+
+static void
+remove_regno_note (insn, note, regno)
+ rtx insn;
+ enum reg_note note;
+ int regno;
+{
+ register rtx *note_link, this;
+
+ note_link = &REG_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 ();
+}
+
+/* Find the hard register number of virtual register REG in REGSTACK.
+ The hard register number is relative to the top of the stack. -1 is
+ returned if the register is not found. */
+
+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;
+}
+
+/* Delete INSN from the RTL. Mark the insn, but don't remove it from
+ the chain of insns. Doing so could confuse block_begin and block_end
+ if this were the only insn in the block. */
+
+static void
+delete_insn_for_stacker (insn)
+ rtx insn;
+{
+ PUT_CODE (insn, NOTE);
+ NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED;
+ NOTE_SOURCE_FILE (insn) = 0;
+ INSN_DELETED_P (insn) = 1;
+}
+
+/* Emit an insn to pop virtual register REG before or after INSN.
+ REGSTACK is the stack state after INSN and is updated to reflect this
+ pop. WHEN is either emit_insn_before or emit_insn_after. A pop insn
+ is represented as a SET whose destination is the register to be popped
+ and source is the top of stack. A death note for the top of stack
+ cases the movdf pattern to pop. */
+
+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 ();
+
+ pop_rtx = gen_rtx (SET, VOIDmode, DFmode_reg[hard_regno],
+ DFmode_reg[FIRST_STACK_REG]);
+
+ pop_insn = (*when) (pop_rtx, insn);
+ PUT_MODE (pop_insn, VOIDmode);
+
+ REG_NOTES (pop_insn) = gen_rtx (EXPR_LIST,
+ REG_DEAD, DFmode_reg[FIRST_STACK_REG],
+ 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;
+}
+
+/* Emit an insn before or after INSN to swap virtual register REG with the
+ top of stack. WHEN should be `emit_insn_before' or `emit_insn_before'
+ REGSTACK is the stack state before the swap, and is updated to reflect
+ the swap. A swap insn is represented as a PARALLEL of two patterns:
+ each pattern moves one reg to the other.
+
+ 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)
+ rtx insn;
+ stack regstack;
+ int hard_regno;
+ rtx (*when)();
+{
+ rtx gen_swapdf();
+ rtx swap_rtx, swap_insn;
+ int tmp, other;
+
+ if (hard_regno == FIRST_STACK_REG)
+ return;
+
+ swap_rtx = gen_swapdf (DFmode_reg[hard_regno], DFmode_reg[FIRST_STACK_REG]);
+ swap_insn = (*when) (swap_rtx, insn);
+ PUT_MODE (swap_insn, VOIDmode);
+
+ other = regstack->top - (hard_regno - FIRST_STACK_REG);
+
+ tmp = regstack->reg[other];
+ regstack->reg[other] = 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. */
+
+static void
+emit_swap_insn (insn, regstack, reg, when)
+ rtx insn;
+ stack regstack;
+ rtx reg;
+ rtx (*when)();
+{
+ int hard_regno;
+
+ hard_regno = get_hard_regnum (regstack, reg);
+
+ emit_hard_swap_insn (insn, regstack, hard_regno, when);
+}
+
+/* Handle a move to or from a stack register in PAT, which is in INSN.
+ REGSTACK is the current stack. */
+
+static void
+move_for_stack_reg (insn, regstack, pat)
+ rtx insn;
+ stack regstack;
+ rtx pat;
+{
+ rtx *src = get_true_reg (&SET_SRC (pat));
+ rtx *dest = get_true_reg (&SET_DEST (pat));
+ rtx note;
+
+ if (STACK_REG_P (*src) && STACK_REG_P (*dest))
+ {
+ /* Write from one stack reg to another. If SRC dies here, then
+ just change the register mapping and delete the insn. */
+
+ note = find_regno_note (insn, REG_DEAD, REGNO (*src));
+ if (note)
+ {
+ int i;
+
+ /* If this is a no-op move, there must not be a REG_DEAD note. */
+ if (REGNO (*src) == REGNO (*dest))
+ abort ();
+
+ for (i = regstack->top; i >= 0; i--)
+ if (regstack->reg[i] == REGNO (*src))
+ break;
+
+ /* The source must be live, and the dest must be dead. */
+ if (i < 0 || get_hard_regnum (regstack, *dest) >= FIRST_STACK_REG)
+ abort ();
+
+ /* It is possible that the dest is unused after this insn.
+ If so, just pop the src. */
+
+ 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;
+ }
+
+ /* The source reg does not die. */
+
+ /* If this appears to be a no-op move, delete it, or else it
+ will confuse the machine description output patterns. But if
+ it is REG_UNUSED, we must pop the reg now, as per-insn processing
+ for REG_UNUSED will not work for deleted insns. */
+
+ 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;
+ }
+
+ /* The destination ought to be dead */
+ if (get_hard_regnum (regstack, *dest) >= FIRST_STACK_REG)
+ abort ();
+
+ replace_reg (src, get_hard_regnum (regstack, *src));
+
+ regstack->reg[++regstack->top] = REGNO (*dest);
+ SET_HARD_REG_BIT (regstack->reg_set, REGNO (*dest));
+ replace_reg (dest, FIRST_STACK_REG);
+ }
+ else if (STACK_REG_P (*src))
+ {
+ /* Save from a stack reg to MEM, or possibly integer reg. Since
+ only top of stack may be saved, emit an exchange first if
+ needs be. */
+
+ emit_swap_insn (insn, regstack, *src, emit_insn_before);
+
+ 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));
+ }
+
+ replace_reg (src, FIRST_STACK_REG);
+ }
+ else if (STACK_REG_P (*dest))
+ {
+ /* Load from MEM, or possibly integer REG or constant, into the
+ stack regs. The actual target is always the top of the
+ stack. The stack mapping is changed to reflect that DEST is
+ now at top of stack. */
+
+ /* The destination ought to be dead */
+ 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 (dest, FIRST_STACK_REG);
+ }
+ else
+ abort ();
+}
+
+/* Handle a comparison. Special care needs to be taken to avoid
+ causing comparisons that a 387 cannot do correctly, such as EQ.
+
+ Also, a pop insn may need to be emitted. The 387 does have an
+ `fcompp' insn that can pop two regs, but it is sometimes too expensive
+ to do this - a `fcomp' followed by a `fstpl %st(0)' may be easier to
+ set up. */
+
+static void
+compare_for_stack_reg (insn, regstack, pat)
+ rtx insn;
+ stack regstack;
+ rtx pat;
+{
+ rtx *src1, *src2;
+ rtx src1_note, src2_note;
+
+ 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 (! STACK_REG_P (*src1))
+ abort ();
+
+ /* We will fix any death note later. */
+
+ 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 = 0;
+
+ emit_swap_insn (insn, regstack, *src1, emit_insn_before);
+
+ replace_reg (src1, FIRST_STACK_REG);
+
+ if (STACK_REG_P (*src2))
+ replace_reg (src2, get_hard_regnum (regstack, *src2));
+
+ if (src1_note)
+ {
+ CLEAR_HARD_REG_BIT (regstack->reg_set, REGNO (XEXP (src1_note, 0)));
+ replace_reg (&XEXP (src1_note, 0), FIRST_STACK_REG);
+ regstack->top--;
+ }
+
+ /* If the second operand dies, handle that. But if the operands are
+ the same stack register, don't bother, because only one death is
+ needed, and it was just handled. */
+
+ if (src2_note
+ && ! (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
+ next to top of stack and the top of stack also dies. Since
+ we have already popped src1, "next to top of stack" is really
+ at top (FIRST_STACK_REG) now. */
+
+ if (get_hard_regnum (regstack, XEXP (src2_note, 0)) == FIRST_STACK_REG
+ && src1_note)
+ {
+ CLEAR_HARD_REG_BIT (regstack->reg_set, REGNO (XEXP (src2_note, 0)));
+ replace_reg (&XEXP (src2_note, 0), FIRST_STACK_REG + 1);
+ regstack->top--;
+ }
+ else
+ {
+ /* The 386 can only represent death of the first operand in
+ the case handled above. In all other cases, emit a separate
+ pop and remove the death note from here. */
+
+ remove_regno_note (insn, REG_DEAD, REGNO (XEXP (src2_note, 0)));
+
+ emit_pop_insn (insn, regstack, XEXP (src2_note, 0),
+ emit_insn_after);
+ }
+ }
+}
+
+/* Substitute new registers in PAT, which is part of INSN. REGSTACK
+ is the current register layout. */
+
+static void
+subst_stack_regs_pat (insn, regstack, pat)
+ rtx insn;
+ stack regstack;
+ rtx pat;
+{
+ rtx *dest, *src;
+ rtx *src1 = 0, *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));
+
+ /* See if this is a `movM' pattern, and handle elsewhere if so. */
+
+ 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:
+ regstack->reg[++regstack->top] = REGNO (*dest);
+ SET_HARD_REG_BIT (regstack->reg_set, REGNO (*dest));
+ replace_reg (dest, FIRST_STACK_REG);
+ break;
+
+ case REG:
+ /* This is a `tstM2' case. */
+ if (*dest != cc0_rtx)
+ abort ();
+
+ src1 = src;
+
+ /* Fall through. */
+
+ case SQRT:
+ case ABS:
+ case NEG:
+ /* These insns only operate on the top of the stack. DEST might
+ be cc0_rtx if we're processing a tstM pattern. Also, it's
+ possible that the tstM case results in a REG_DEAD note on the
+ source. */
+
+ if (src1 == 0)
+ src1 = get_true_reg (&XEXP (SET_SRC (pat), 0));
+
+ emit_swap_insn (insn, regstack, *src1, emit_insn_before);
+
+ 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:
+ /* On i386, reversed forms of subM3 and divM3 exist for
+ MODE_FLOAT, so the same code that works for addM3 and mulM3
+ can be used. */
+ case MULT:
+ case PLUS:
+ /* These insns can accept the top of stack as a destination
+ from a stack reg or mem, or can use the top of stack as a
+ source and some other stack register (possibly top of stack)
+ as a destination. */
+
+ src1 = get_true_reg (&XEXP (SET_SRC (pat), 0));
+ src2 = get_true_reg (&XEXP (SET_SRC (pat), 1));
+
+ /* We will fix any death note later. */
+
+ if (STACK_REG_P (*src1))
+ src1_note = find_regno_note (insn, REG_DEAD, REGNO (*src1));
+ else
+ src1_note = 0;
+ if (STACK_REG_P (*src2))
+ src2_note = find_regno_note (insn, REG_DEAD, REGNO (*src2));
+ else
+ src2_note = 0;
+
+ /* 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);
+ 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. */
+
+ 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, emit_insn_before);
+ }
+
+ 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 the register that dies is at the top of stack, then
+ the destination is somewhere else - merely substitute it.
+ But if the reg that dies is not at top of stack, then
+ move the top of stack to the dead reg, as though we had
+ done the insn and then a store-with-pop. */
+
+ 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;
+
+ default:
+ abort ();
+ }
+}
+
+/* Substitute stack hard reg numbers for stack virtual registers in
+ INSN. Non-stack register numbers are not changed. REGSTACK is the
+ current stack content. Insns may be emitted as needed to arrange the
+ stack for the 387 based on the contents of the insn. */
+
+static void
+subst_stack_regs (insn, regstack)
+ rtx insn;
+ stack regstack;
+{
+ register rtx *note_link, note;
+ register int i;
+
+ if ((GET_CODE (insn) != INSN && GET_CODE (insn) != CALL_INSN)
+ || INSN_DELETED_P (insn))
+ return;
+
+ /* The stack should be empty at a call. */
+
+ if (GET_CODE (insn) == CALL_INSN)
+ for (i = FIRST_STACK_REG; i <= LAST_STACK_REG; i++)
+ if (TEST_HARD_REG_BIT (regstack->reg_set, i))
+ abort ();
+
+ /* Do the actual substitution if any stack regs are mentioned.
+ Since we only record whether entire insn mentions stack regs, and
+ subst_stack_regs_pat only works for patterns that contain stack regs,
+ we must check each pattern in a parallel here. A call_value_pop could
+ fail otherwise. */
+
+ if (GET_MODE (insn) == QImode)
+ {
+ 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));
+ }
+ else
+ subst_stack_regs_pat (insn, regstack, PATTERN (insn));
+ }
+
+ /* subst_stack_regs_pat may have deleted a no-op insn. If so, any
+ REG_UNUSED will already have been dealt with, so just return. */
+
+ if (INSN_DELETED_P (insn))
+ return;
+
+ /* If there is a REG_UNUSED note on a stack register on this insn,
+ the indicated reg must be popped. The REG_UNUSED note is removed,
+ since the form of the newly emitted pop insn references the reg,
+ making it no longer `unset'. */
+
+ note_link = &REG_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);
+ emit_pop_insn (insn, regstack, XEXP (note, 0),
+ emit_insn_after);
+ }
+ else
+ note_link = &XEXP (note, 1);
+}
+
+/* Change the organization of the stack so that it fits a new basic
+ block. Some registers might have to be popped, but there can never be
+ a register live in the new block that is not now live.
+
+ Insert any needed insns after INSN. OLD is the original stack
+ layout, and NEW is the desired form. OLD is updated to reflect the
+ code emitted, ie, it will be the same as NEW upon return.
+
+ This function will not preserve block_end[]. But that information
+ is no longer needed once this has executed. */
+
+static void
+change_stack (insn, old, new)
+ rtx insn;
+ stack old;
+ stack new;
+{
+ int reg;
+
+ /* We will be inserting new insns after INSN, by first finding the
+ next insn, and inserting before it. */
+
+ insn = NEXT_INSN (insn);
+
+ /* Pop any registers that are not needed in the new block. */
+
+ for (reg = old->top; reg >= 0; reg--)
+ if (! TEST_HARD_REG_BIT (new->reg_set, old->reg[reg]))
+ emit_pop_insn (insn, old, DFmode_reg[old->reg[reg]],
+ emit_insn_before);
+
+ if (new->top == -2)
+ {
+ /* If the new block has never been processed, then it can inherit
+ the old stack order. */
+
+ new->top = old->top;
+ bcopy (old->reg, new->reg, sizeof (new->reg));
+ }
+ else
+ {
+ /* This block has been entered before, and we must match the
+ previously selected stack order. */
+
+ /* By now, the only difference should be the order of the stack,
+ not their depth or liveliness. */
+
+ GO_IF_HARD_REG_EQUAL (old->reg_set, new->reg_set, win);
+
+ abort ();
+
+ win:
+
+ if (old->top != new->top)
+ abort ();
+
+ /* Loop here emitting swaps until the stack is correct. The
+ worst case number of swaps emitted is N + 2, where N is the
+ depth of the stack. In some cases, the reg at the top of
+ stack may be correct, but swapped anyway in order to fix
+ other regs. But since we never swap any other reg away from
+ its correct slot, this algorithm will converge. */
+
+ do
+ {
+ /* Swap the reg at top of stack into the position it is
+ supposed to be in, until the correct top of stack appears. */
+
+ 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, DFmode_reg[old->reg[reg]],
+ emit_insn_before);
+ }
+
+ /* See if any regs remain incorrect. If so, bring an
+ incorrect reg to the top of stack, and let the while loop
+ above fix it. */
+
+ for (reg = new->top; reg >= 0; reg--)
+ if (new->reg[reg] != old->reg[reg])
+ {
+ emit_swap_insn (insn, old, DFmode_reg[old->reg[reg]],
+ emit_insn_before);
+ break;
+ }
+ } while (reg >= 0);
+
+ /* At this point there must be no differences. */
+
+ for (reg = old->top; reg >= 0; reg--)
+ if (old->reg[reg] != new->reg[reg])
+ abort ();
+ }
+}
+
+/* Check PAT, which points to RTL in INSN, for a LABEL_REF. If it is
+ found, ensure that a jump from INSN to the code_label to which the
+ label_ref points ends up with the same stack as that at the
+ code_label. Do this by inserting insns just before the code_label to
+ pop and rotate the stack until it is in the correct order. REGSTACK
+ is the order of the register stack in INSN.
+
+ Any code that is emitted here must not be later processed as part
+ of any block, as it will already contain hard register numbers. */
+
+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;
+
+ if (GET_CODE (pat) != LABEL_REF)
+ {
+ 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;
+ }
+
+ label = XEXP (pat, 0);
+ if (GET_CODE (label) != CODE_LABEL)
+ abort ();
+
+ /* First, see if in fact anything needs to be done to the stack at all. */
+
+ label_stack = &block_stack_in[BLOCK_NUM (label)];
+
+ if (label_stack->top == -2)
+ {
+ /* If the target block hasn't had a stack order selected, then
+ we need merely ensure that no pops are needed. */
+
+ 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 will not emit any code in this case. */
+
+ change_stack (label, regstack, label_stack);
+ 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;
+ }
+
+ /* At least one insn will need to be inserted before label. Insert
+ a jump around the code we are about to emit. Emit a label for the new
+ code, and point the original insn at this new label. We can't use
+ redirect_jump here, because we're using fld[4] of the code labels as
+ LABEL_REF chains, no NUSES counters. */
+
+ 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;
+
+ /* The old label_ref will no longer point to the code_label if now uses,
+ so strip the label_ref from the code_label's chain of references. */
+
+ 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;
+
+ /* Now emit the needed code. */
+
+ temp_stack = *regstack;
+
+ change_stack (new_label, &temp_stack, label_stack);
+}
+
+/* Traverse all basic blocks in a function, converting the register
+ refereces in each insn from the "flat" register file that gcc uses, to
+ the stack-like registers the 387 uses. */
+
+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)
+ {
+ /* This block has not been previously encountered. Choose a
+ default mapping for any stack regs live on entry */
+
+ 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;
+ }
+
+ /* Process all insns in this block. Keep track of `next' here,
+ so that we don't process any insns emitted while making
+ substitutions in INSN. */
+
+ next = block_begin[block];
+ regstack = block_stack_in[block];
+ do
+ {
+ insn = next;
+ next = NEXT_INSN (insn);
+
+ /* Don't bother processing unless there is a stack reg
+ mentioned.
+
+ ??? For now, process CALL_INSNs too to make sure that the
+ stack regs are dead after a call. Remove this eventually. */
+
+ if (GET_MODE (insn) == QImode || GET_CODE (insn) == CALL_INSN)
+ subst_stack_regs (insn, &regstack);
+
+ } while (insn != block_end[block]);
+
+ /* Something failed if the stack life doesn't match. */
+
+ GO_IF_HARD_REG_EQUAL (regstack.reg_set, block_out_reg_set[block], win);
+
+ abort ();
+
+ win:
+
+ /* Adjust the stack of this block on exit to match the stack of
+ the target block, or copy stack information into stack of
+ jump target if the target block's stack order hasn't been set
+ yet. */
+
+ if (GET_CODE (insn) == JUMP_INSN)
+ goto_block_pat (insn, &regstack, PATTERN (insn));
+
+ /* Likewise handle the case where we fall into the next block. */
+
+ if ((block < blocks - 1) && block_drops_in[block+1])
+ change_stack (insn, &regstack, &block_stack_in[block+1]);
+ }
+
+ /* If the last basic block is the end of a loop, and that loop has
+ regs live at its start, then the last basic block will have regs live
+ at its end that need to be popped before the function returns. */
+
+ for (reg = regstack.top; reg >= 0; reg--)
+ if (! current_function_returns_real
+ || regstack.reg[reg] != FIRST_STACK_REG)
+ insn = emit_pop_insn (insn, &regstack, DFmode_reg[regstack.reg[reg]],
+ emit_insn_after);
+}
+
+/* Check expression PAT, which is in INSN, for label references. if
+ one is found, print the block number of destination to FILE. */
+
+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));
+ }
+ }
+}
+
+/* Write information about stack registers and stack blocks into FILE.
+ This is part of making a debugging dump. */
+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");
+ }
+}
+
+/* Report an error at line LINE of file FILE.
+ S is a string and an arg for `printf'. */
+
+/* Report an fatal error at the line number of the insn INSN (ASM_OPERAND).
+ S1, S2 is a string and an arg for `printf'. */
+
+static void
+fatal_for_asm (insn, s1, s2)
+ rtx insn;
+ char *s1, *s2;
+{
+ char *filename;
+ int line;
+ rtx body = PATTERN (insn);
+ rtx asmop = 0;
+
+ /* Find the (or one of the) ASM_OPERANDS in the insn. */
+ if (GET_CODE (body) == SET && GET_CODE (SET_SRC (body)) == ASM_OPERANDS)
+ asmop = SET_SRC (body);
+ else if (GET_CODE (body) == ASM_OPERANDS)
+ asmop = body;
+ else if (GET_CODE (body) == PARALLEL
+ && GET_CODE (XVECEXP (body, 0, 0)) == SET)
+ asmop = SET_SRC (XVECEXP (body, 0, 0));
+ else if (GET_CODE (body) == PARALLEL
+ && GET_CODE (XVECEXP (body, 0, 0)) == ASM_OPERANDS)
+ asmop = XVECEXP (body, 0, 0);
+ else
+ abort ();
+
+ filename = ASM_OPERANDS_SOURCE_FILE (asmop);
+ line = ASM_OPERANDS_SOURCE_LINE (asmop);
+
+ fprintf (stderr, s1);
+ debug_rtx (insn);
+
+ error_with_file_and_line (filename, line, s2, NULL, NULL);
+ exit (34);
+}
+#endif /* STACK_REGS */