]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
Initial revision
authorJames Van Artsdalen <jrv@gnu.org>
Thu, 26 Dec 1991 10:05:42 +0000 (10:05 +0000)
committerJames Van Artsdalen <jrv@gnu.org>
Thu, 26 Dec 1991 10:05:42 +0000 (10:05 +0000)
From-SVN: r142

gcc/reg-stack.c [new file with mode: 0644]

diff --git a/gcc/reg-stack.c b/gcc/reg-stack.c
new file mode 100644 (file)
index 0000000..f69fd9f
--- /dev/null
@@ -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. */
+\f
+#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 ();
+\f
+\f
+/* 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;
+}
+\f
+/* 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);
+}
+\f
+/* 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));
+       }
+    }
+}
+\f
+/* 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 ();
+}
+\f
+/* 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 ();
+    }
+}
+\f
+/* 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);
+    }
+}
+\f
+/* 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));
+    }
+}
+\f
+/* 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:
+  ;
+}
+\f
+/*****************************************************************************
+   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;
+}
+\f
+/* 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;
+}
+\f
+/* 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);
+}
+\f
+/* 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 ();
+}
+\f
+/* 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);
+       }
+    }
+}
+\f
+/* 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 ();
+      }
+}
+\f
+/* 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);
+}
+\f
+/* 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 ();
+    }
+}
+\f
+/* 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);
+}
+\f
+/* 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);
+}
+\f
+/* 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));
+       }
+    }
+}
+\f
+/* 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");
+    }
+}
+\f
+/* 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 */