386BSD 0.1 development
authorWilliam F. Jolitz <wjolitz@soda.berkeley.edu>
Wed, 8 May 1991 17:49:40 +0000 (09:49 -0800)
committerWilliam F. Jolitz <wjolitz@soda.berkeley.edu>
Wed, 8 May 1991 17:49:40 +0000 (09:49 -0800)
Work on file usr/src/usr.bin/gcc/cc1/config/i386.md
Work on file usr/src/usr.bin/gcc/cc1/config/out-i386.c

Co-Authored-By: Lynne Greer Jolitz <ljolitz@cardio.ucsf.edu>
Synthesized-from: 386BSD-0.1

usr/src/usr.bin/gcc/cc1/config/i386.md [new file with mode: 0644]
usr/src/usr.bin/gcc/cc1/config/out-i386.c [new file with mode: 0644]

diff --git a/usr/src/usr.bin/gcc/cc1/config/i386.md b/usr/src/usr.bin/gcc/cc1/config/i386.md
new file mode 100644 (file)
index 0000000..fc9c9e1
--- /dev/null
@@ -0,0 +1,1986 @@
+;;
+;; This code is derived from software copyrighted by the Free Software
+;; Foundation.
+;;
+;; Modified 1991 by Donn Seeley at UUNET Technologies, Inc.
+;;
+;;     @(#)i386.md     6.2 (Berkeley) 5/8/91
+;;
+
+;; GCC machine description for Intel 80386.
+;; Copyright (C) 1988 Free Software Foundation, Inc.
+;; Mostly by William Schelter.
+
+;; 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 1, 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.
+
+
+;;- instruction definitions
+
+;;- @@The original PO technology requires these to be ordered by speed,
+;;- @@    so that assigner will pick the fastest.
+
+;;- See file "rtl.def" for documentation on define_insn, match_*, et. al.
+
+;;- When naming insn's (operand 0 of define_insn) be careful about using
+;;- names from other targets machine descriptions.
+
+;;- cpp macro #define NOTICE_UPDATE_CC in file tm.h handles condition code
+;;- updates for most instructions.
+
+;;- Operand classes for the register allocator:
+;;- 'a' for eax
+;;- 'd' for edx
+;;- 'c' for ecx
+;;- 'b' for ebx
+;;- 'f' for anything in FLOAT_REGS
+;;- 'r' any (non-floating-point) register
+;;- 'q' regs that allow byte operations (A, B, C and D)
+;;- 'A' A and D registers
+
+;; the special asm out single letter directives following a '%' are:
+;; 'z' mov%z1 would be movl, movw, or movb depending on the mode of operands[1]
+;; 's' output a '*'
+;; 'w' If the operand is a REG, it uses the mode size to determine the
+;;      printing of the reg
+
+\f
+;; Put tstsi first among test insns so it matches a CONST_INT operand.
+
+(define_insn "tstsi"
+  [(set (cc0)
+       (match_operand:SI 0 "general_operand" "rm"))]
+  ""
+  "*
+{
+  operands[1] = const0_rtx;
+  if (REG_P (operands[0]))
+    return AS2 (test%L0,%0,%0);
+  return AS2 (cmp%L0,%1,%0);
+}")
+
+(define_insn "tsthi"
+  [(set (cc0)
+       (match_operand:HI 0 "general_operand" "rm"))]
+  ""
+  "*
+{
+  operands[1] = const0_rtx;
+  if (REG_P (operands[0]))
+    return AS2 (test%W0,%0,%0);
+  return AS2 (cmp%W0,%1,%0);
+}")
+
+(define_insn "tstqi"
+  [(set (cc0)
+       (match_operand:QI 0 "general_operand" "qm"))]
+  ""
+  "*
+{
+  operands[1] = const0_rtx;
+  if (REG_P (operands[0]))
+    return AS2 (test%B0,%0,%0);
+  return AS2 (cmp%B0,%1,%0);
+}")
+
+(define_insn "tstsf"
+  [(set (cc0)
+       (match_operand:SF 0 "general_operand" "rm,f"))
+   (clobber (reg:SI 0))]
+  "TARGET_80387"
+  "*
+{
+  rtx xops[1];
+  if (!FP_REG_P (operands[0]))
+    fp_push_sf (operands[0]);
+/*  fp_pop_level--; */
+  xops[0] = FP_TOP;
+  cc_status.flags |= CC_IN_80387;
+  if (FP_REG_P (operands[0]) && ! top_dead_p (insn))
+    output_asm_insn (\"ftst\;fnstsw %R0ax\;sahf\", xops);
+  else
+    output_asm_insn (\"ftst\;fstp %0(0)\;fnstsw %R0ax\;sahf\", xops);
+  RETCOM (testsf);
+}")
+
+(define_insn "tstdf"
+  [(set (cc0)
+       (match_operand:DF 0 "general_operand" "rm,f"))
+   (clobber (reg:SI 0))
+   ]
+  "TARGET_80387"
+  "*
+{
+  rtx xops[1];
+  if (!FP_REG_P (operands[0]))
+    fp_push_df (operands[0]);
+/*  fp_pop_level--; */
+  xops[0] = FP_TOP;
+  cc_status.flags |= CC_IN_80387;
+  if (FP_REG_P (operands[0]) && ! top_dead_p (insn))
+    output_asm_insn (\"ftst\;fnstsw %R0ax\;sahf\", xops);
+  else
+    output_asm_insn (\"ftst\;fstp %0(0)\;fnstsw %R0ax\;sahf\", xops);
+  RETCOM (testdf);
+}")
+\f
+;;- compare instructions
+
+;; Put cmpsi first among compare insns so it matches two CONST_INT operands.
+
+(define_insn "cmpsi"
+  [(set (cc0)
+       (compare (match_operand:SI 0 "general_operand" "mr,ri")
+                (match_operand:SI 1 "general_operand" "ri,mr")))]
+  ""
+  "*
+{
+  if (REG_P (operands[1])
+      || (!REG_P (operands[0]) && GET_CODE (operands[0]) != MEM))
+    {
+      cc_status.flags |= CC_REVERSED;
+      return AS2 (cmp%L0,%0,%1);
+    }
+  return AS2 (cmp%L0,%1,%0);
+}")
+
+(define_insn "cmphi"
+  [(set (cc0)
+       (compare (match_operand:HI 0 "general_operand" "mr,ri")
+                (match_operand:HI 1 "general_operand" "ri,mr")))]
+  ""
+  "*
+{
+  if (REG_P (operands[1])
+      || (!REG_P (operands[0]) && GET_CODE (operands[0]) != MEM))
+    {
+      cc_status.flags |= CC_REVERSED;
+      return AS2 (cmp%W0,%0,%1);
+    }
+  return AS2 (cmp%W0,%1,%0);
+}")
+
+(define_insn "cmpqi"
+  [(set (cc0)
+       (compare (match_operand:QI 0 "general_operand" "qn,mq")
+                (match_operand:QI 1 "general_operand" "qm,nq")))]
+  ""
+  "*
+{
+  if (REG_P (operands[1])
+      || (!REG_P (operands[0]) && GET_CODE (operands[0]) != MEM))
+    {
+      cc_status.flags |= CC_REVERSED;
+      return AS2 (cmp%B0,%0,%1);
+    }
+  return AS2 (cmp%B0,%1,%0);
+}")
+
+(define_insn "cmpdf"
+  [(set (cc0)
+       (compare (match_operand:DF 0 "general_operand" "m,f*r,m,f,r,!*r")
+                (match_operand:DF 1 "general_operand" "m,m,f*r,r,f,*r")))
+      (clobber (reg:SI 0))]
+  "TARGET_80387"
+  "*
+{
+  if (FP_REG_P (operands[0]))
+    {
+      rtx tem = operands[1];
+      operands[1] = operands[0];
+      operands[0] = tem;
+      cc_status.flags |= CC_REVERSED;
+    }
+  if (! FP_REG_P (operands[1]))
+    output_movdf (FP_TOP, operands[1]);
+  output_movdf (FP_TOP, operands[0]);
+/*  fp_pop_level--;
+  fp_pop_level--; */
+  cc_status.flags |= CC_IN_80387;
+  return \"fcompp\;fnstsw %R0ax\;sahf\";
+}")
+
+(define_insn "cmpsf"
+  [(set (cc0)
+       (compare (match_operand:SF 0 "general_operand" "m,f*r,m,f,r,!*r")
+                (match_operand:SF 1 "general_operand" "m,m,f*r,r,f,*r")))
+   (clobber (reg:SI 0))]
+  "TARGET_80387"
+  "*
+{
+  if (FP_REG_P (operands[0]))
+    {
+      rtx tem = operands[1];
+      operands[1] = operands[0];
+      operands[0] = tem;
+      cc_status.flags |= CC_REVERSED;
+    }
+  if (! FP_REG_P (operands[1]))
+    output_movsf (FP_TOP, operands[1]);
+  output_movsf (FP_TOP, operands[0]);
+/*  fp_pop_level--;
+  fp_pop_level--; */
+  cc_status.flags |= CC_IN_80387;
+  return \"fcompp\;fnstsw %R0ax\;sahf\";
+}")
+
+;; logical compare
+(define_insn ""
+  [(set (cc0)
+       (and:SI (match_operand:SI 0 "general_operand" "rm,ri")
+               (match_operand:SI 1 "general_operand" "ri,rm")))]
+  ""
+  "*
+{
+  if (CONSTANT_P (operands[1]) || GET_CODE (operands[0]) == MEM)
+    return AS2 (test%L0,%1,%0);
+  return AS2 (test%L0,%0,%1);
+}")
+
+(define_insn ""
+  [(set (cc0)
+       (and:HI (match_operand:HI 0 "general_operand" "rm,ri")
+               (match_operand:HI 1 "general_operand" "ri,rm")))]
+  ""
+  "*
+{
+  if (CONSTANT_P (operands[1]) || GET_CODE (operands[0]) == MEM)
+    return AS2 (test%W0,%1,%0);
+  return AS2 (test%W0,%0,%1);
+}")
+
+(define_insn ""
+  [(set (cc0)
+       (and:QI (match_operand:QI 0 "general_operand" "qm,qi")
+               (match_operand:QI 1 "general_operand" "qi,qm")))]
+  ""
+  "*
+{
+  if (CONSTANT_P (operands[1]) || GET_CODE (operands[0]) == MEM)
+    return AS2 (test%B0,%1,%0);
+  return AS2 (test%B0,%0,%1);
+}")
+\f
+;; move instructions.
+;; There is one for each machine mode,
+;; and each is preceded by a corresponding push-insn pattern
+;; (since pushes are not general_operands on the 386).
+
+(define_insn ""
+  [(set (match_operand:SI 0 "push_operand" "=<")
+       (match_operand:SI 1 "general_operand" "g"))]
+  ""
+  "push%L0 %1")
+
+;; General case of fullword move.
+(define_insn "movsi"
+  [(set (match_operand:SI 0 "general_operand" "=g,r")
+       (match_operand:SI 1 "general_operand" "ri,m"))]
+  ""
+  "*
+{
+  rtx link;
+  if (operands[1] == const0_rtx && REG_P (operands[0]))
+    return \"xor%L0 %0,%0\";
+  if (operands[1] == const1_rtx
+      && (link = find_reg_note (insn, REG_WAS_0, 0))
+      /* Make sure the insn that stored the 0 is still present.  */
+      && ! XEXP (link, 0)->volatil
+      && GET_CODE (XEXP (link, 0)) != NOTE
+      /* Make sure cross jumping didn't happen here.  */
+      && no_labels_between_p (XEXP (link, 0), insn))
+    /* Fastest way to change a 0 to a 1.  */
+    return \"inc%L0 %0\";
+  return \"mov%L0 %1,%0\";
+}")
+
+(define_insn ""
+  [(set (match_operand:HI 0 "push_operand" "=<")
+       (match_operand:HI 1 "general_operand" "g"))]
+  ""
+  "push%W0 %1")
+
+(define_insn "movhi"
+  [(set (match_operand:HI 0 "general_operand" "=g,r")
+       (match_operand:HI 1 "general_operand" "ri,m"))]
+  ""
+  "*
+{
+  rtx link;
+  if (operands[1] == const0_rtx && REG_P (operands[0]))
+    return \"xor%W0 %0,%0\";
+  if (operands[1] == const1_rtx
+      && (link = find_reg_note (insn, REG_WAS_0, 0))
+      /* Make sure the insn that stored the 0 is still present.  */
+      && ! XEXP (link, 0)->volatil
+      && GET_CODE (XEXP (link, 0)) != NOTE
+      /* Make sure cross jumping didn't happen here.  */
+      && no_labels_between_p (XEXP (link, 0), insn))
+    /* Fastest way to change a 0 to a 1.  */
+    return \"inc%W0 %0\";
+  return \"mov%W0 %1,%0\";
+}")
+
+;; emit_push_insn when it calls move_by_pieces
+;; requires an insn to "push a byte".
+;; But actually we use pushw, which has the effect of rounding
+;; the amount pushed up to a halfword.
+(define_insn ""
+  [(set (match_operand:QI 0 "push_operand" "=<")
+       (match_operand:QI 1 "general_operand" "q"))]
+  ""
+  "*
+{
+  operands[1] = gen_rtx (REG, HImode, REGNO (operands[1]));
+  return \"push%W0 %1\";
+}")
+
+(define_insn "movqi"
+  [(set (match_operand:QI 0 "general_operand" "=q,*r,m")
+       (match_operand:QI 1 "general_operand" "*g,q,qi"))]
+  ""
+  "*
+{
+  rtx link;
+  if (operands[1] == const0_rtx && REG_P (operands[0]))
+    return \"xor%B0 %0,%0\";
+  if (operands[1] == const1_rtx
+      && (link = find_reg_note (insn, REG_WAS_0, 0))
+      /* Make sure the insn that stored the 0 is still present.  */
+      && ! XEXP (link, 0)->volatil
+      && GET_CODE (XEXP (link, 0)) != NOTE
+      /* Make sure cross jumping didn't happen here.  */
+      && no_labels_between_p (XEXP (link, 0), insn))
+    /* Fastest way to change a 0 to a 1.  */
+    return \"inc%B0 %0\";
+  /* If mov%B0 isn't allowed for one of these regs, use mov%W0.  */
+  if (NON_QI_REG_P (operands[0]) || NON_QI_REG_P (operands[1]))
+    return (AS2 (mov%W0,%w1,%w0));
+  return (AS2 (mov%B0,%1,%0));
+}")
+
+; I suspect nothing can ever match this ???
+;(define_insn ""
+;  [(set (match_operand:SF 0 "general_operand" "rm")
+;      (match_operand:SF 1 "general_operand" "f"))
+;   (clobber (reg:SF 8))]
+;  ""
+;  "*
+;{
+;  output_asm_insn ("???", operands);
+;  fpop_sf (operands[0]);
+;  RETCOM (movsf_clobber);
+;}")
+
+(define_insn ""
+  [(set (match_operand:SF 0 "push_operand" "=<,<")
+       (match_operand:SF 1 "general_operand" "gF,f"))]
+  ""
+  "*
+{
+  if (FP_REG_P (operands[1]))
+    {
+      rtx xops[3];
+      xops[0] = AT_SP (SFmode);
+      xops[1] = gen_rtx (CONST_INT, VOIDmode, 4);
+      xops[2] = stack_pointer_rtx;
+/*      fp_pop_level--; */
+      output_asm_insn (AS2 (sub%L0,%1,%2), xops);
+      if (top_dead_p (insn))
+        output_asm_insn (\"fstp%S0 %0\", xops);
+      else
+        output_asm_insn (\"fst%S0 %0\", xops);
+      RET;
+    }
+  return \"push%L0 %1\";
+}")
+
+(define_insn "movsf"
+  ;; `rf' is duplicated in the second alternative
+  ;; to make sure an optional reload is generated
+  ;; for the memref in operand 0.  Otherwise
+  ;; we could use too many hard regs.
+  [(set (match_operand:SF 0 "general_operand" "=rf,mrf,!rm")
+       (match_operand:SF 1 "general_operand" "mrf,rf,F"))]
+  ""
+  "*
+{
+  if (FP_REG_P (operands[1])
+      && !FP_REG_P (operands[0])
+      && !top_dead_p (insn))
+    fp_store_sf (operands[0]);
+  else
+    output_movsf (operands[0], operands[1]);
+  RETCOM (movsf);
+}")
+
+;;should change to handle the memory operands[1] without doing df push..
+(define_insn ""
+  [(set (match_operand:DF 0 "push_operand" "=<,<")
+       (match_operand:DF 1 "general_operand" "gF,f"))]
+  ""
+  "*
+{
+  if (FP_REG_P (operands[1]))
+    {
+      rtx xops[3];
+      xops[0] = AT_SP (DFmode);
+      xops[1] = gen_rtx (CONST_INT, VOIDmode, 8);
+      xops[2] = stack_pointer_rtx;
+/*      fp_pop_level--; */
+      output_asm_insn (AS2 (sub%L0,%1,%2), xops);
+      if (top_dead_p(insn))
+        output_asm_insn (\"fstp%Q0 %0\", xops);
+      else
+        output_asm_insn (\"fst%Q0 %0\", xops);
+      RETCOM (pushdf);
+    }
+  else
+    return output_move_double (operands);
+}")
+
+(define_insn "movdf"
+  [(set (match_operand:DF 0 "general_operand" "=rmf,&fr,!rm")
+       ;; `rf' is duplicated in the second alternative
+       ;; to make sure that optional reloads are generated
+       ;; for the memory reference in operand 1.
+       (match_operand:DF 1 "general_operand" "fr,mrf,F"))]
+  ""
+  "*
+{
+  if (FP_REG_P (operands[1])
+      && ! FP_REG_P (operands[0])
+      && ! top_dead_p (insn))
+    fp_store_df (operands[0]);
+  else
+    output_movdf (operands[0], operands[1]);
+  RETCOM (movdf);
+}")
+
+(define_insn ""
+  [(set (match_operand:DI 0 "push_operand" "=<")
+       (match_operand:DI 1 "general_operand" "roiF"))]
+  ""
+  "*
+{
+  return output_move_double (operands);
+}")
+
+(define_insn "movdi"
+  [(set (match_operand:DI 0 "general_operand" "=&r,rm")
+       (match_operand:DI 1 "general_operand" "m,riF"))]
+  ""
+  "*
+{
+   return output_move_double (operands);
+}")
+\f
+;; These go after the move instructions
+;; because the move instructions are better (require no spilling)
+;; when they can apply.  But these go before the add and subtract insns
+;; because it is often shorter to use these when both apply.
+
+;Lennart Augustsson <augustss@cs.chalmers.se>
+;says this pattern just makes slower code:
+;      pushl   %ebp
+;      addl    $-80,(%esp)
+;instead of
+;      leal    -80(%ebp),%eax
+;      pushl   %eax
+;
+;(define_insn ""
+;  [(set (match_operand:SI 0 "push_operand" "=<")
+;      (plus:SI (match_operand:SI 1 "general_operand" "%r")
+;               (match_operand:SI 2 "general_operand" "ri")))]
+;  ""
+;  "*
+;{
+;  rtx xops[4];
+;  xops[0] = operands[0];
+;  xops[1] = operands[1];
+;  xops[2] = operands[2];
+;  xops[3] = gen_rtx (MEM, SImode, stack_pointer_rtx);
+;  output_asm_insn (\"push%z1 %1\", xops);
+;  output_asm_insn (AS2 (add%z3,%2,%3), xops);
+;  RET;
+;}")
+
+(define_insn ""
+  [(set (match_operand:SI 0 "general_operand" "=g")
+       (plus:SI (match_operand:SI 1 "general_operand" "0")
+                (const_int 1)))]
+  ""
+  "inc%L0 %0")
+
+(define_insn ""
+  [(set (match_operand:SI 0 "general_operand" "=g")
+       (plus:SI (match_operand:SI 1 "general_operand" "0")
+                (const_int -1)))]
+  ""
+  "dec%L0 %0")
+
+(define_insn ""
+  [(set (match_operand:SI 0 "general_operand" "=g")
+       (minus:SI (match_operand:SI 1 "general_operand" "0")
+                 (const_int 1)))]
+  ""
+  "dec%L0 %0")
+
+(define_insn ""
+  [(set (match_operand:SI 0 "register_operand" "=r")
+        (match_operand:QI 1 "address_operand" "p"))]
+  ""
+  "*
+{
+  CC_STATUS_INIT;
+  /* Adding a constant to a register is faster with an add.  */
+  if (GET_CODE (operands[1]) == PLUS
+      && GET_CODE (XEXP (operands[1], 1)) == CONST_INT
+      && rtx_equal_p (operands[0], XEXP (operands[1], 0)))
+    {
+      operands[1] = XEXP (operands[1], 1);
+      return AS2 (add%L0,%1,%0);
+    }
+  return \"lea%L0 %a1,%0\";
+}")
+\f
+;;- conversion instructions
+;;- NONE
+
+;;- truncation instructions
+(define_insn "truncsiqi2"
+  [(set (match_operand:QI 0 "general_operand" "=q,m")
+       (truncate:QI
+        (match_operand:SI 1 "general_operand" "qim,qn")))]
+  ""
+  "*
+{
+  if (CONSTANT_P (operands[1]) && GET_CODE (operands[1]) != CONST_INT)
+    return \"mov%L0 %1,%k0\";
+  return \"mov%B0 %b1,%0\";
+}")
+
+(define_insn "trunchiqi2"
+  [(set (match_operand:QI 0 "general_operand" "=q,m")
+       (truncate:QI
+        (match_operand:HI 1 "general_operand" "qim,qn")))]
+  ""
+  "*
+{
+  if (CONSTANT_P (operands[1]) && GET_CODE (operands[1]) != CONST_INT)
+    return \"mov%W0 %1,%w0\";
+  return \"mov%B0 %b1,%0\";
+}")
+
+(define_insn "truncsihi2"
+  [(set (match_operand:HI 0 "general_operand" "=r,m")
+       (truncate:HI
+        (match_operand:SI 1 "general_operand" "rim,rn")))]
+  ""
+  "*
+{
+  if (CONSTANT_P (operands[1]) && GET_CODE (operands[1]) != CONST_INT)
+    return \"mov%L0 %1,%k0\";
+  return \"mov%W0 %w1,%0\";
+}")
+\f
+;;- zero extension instructions
+;; Note that the one starting from HImode comes before those for QImode
+;; so that a constant operand will match HImode, not QImode.
+
+(define_insn "zero_extendhisi2"
+  [(set (match_operand:SI 0 "general_operand" "=r")
+       (zero_extend:SI
+        (match_operand:HI 1 "general_operand" "rm")))]
+  ""
+  "movz%W0%L0 %1,%0")
+
+(define_insn "zero_extendqihi2"
+  [(set (match_operand:HI 0 "general_operand" "=r")
+       (zero_extend:HI
+        (match_operand:QI 1 "general_operand" "qm")))]
+  ""
+  "movz%B0%W0 %1,%0")
+
+(define_insn "zero_extendqisi2"
+  [(set (match_operand:SI 0 "general_operand" "=r")
+       (zero_extend:SI
+        (match_operand:QI 1 "general_operand" "qm")))]
+  ""
+  "movz%B0%L0 %1,%0")
+\f
+;;- sign extension instructions
+;; Note that the one starting from HImode comes before those for QImode
+;; so that a constant operand will match HImode, not QImode.
+
+/*
+(define_insn "extendsidi2"
+  [(set (match_operand:DI 0 "general_operand" "=a")
+       (sign_extend:DI
+        (match_operand:SI 1 "general_operand" "a")))]
+  ""
+  "clq")
+*/
+
+;; Note that the i386 programmers' manual says that the opcodes
+;; are named movsx..., but the assembler on Unix does not accept that.
+;; We use what the Unix assembler expects.
+
+(define_insn "extendhisi2"
+  [(set (match_operand:SI 0 "general_operand" "=r")
+       (sign_extend:SI
+        (match_operand:HI 1 "general_operand" "rm")))]
+  ""
+  "movs%W0%L0 %1,%0")
+
+(define_insn "extendqihi2"
+  [(set (match_operand:HI 0 "general_operand" "=r")
+       (sign_extend:HI
+        (match_operand:QI 1 "general_operand" "qm")))]
+  ""
+  "movs%B0%W0 %1,%0")
+
+(define_insn "extendqisi2"
+  [(set (match_operand:SI 0 "general_operand" "=r")
+       (sign_extend:SI
+        (match_operand:QI 1 "general_operand" "qm")))]
+  ""
+ "movs%B0%L0 %1,%0"
+ )
+\f
+;; Conversions between float and double.
+
+(define_insn "extendsfdf2"
+  [(set (match_operand:DF 0 "general_operand" "=fm,f,fm,fm")
+       (float_extend:DF
+        (match_operand:SF 1 "general_operand" "m,0,f,!*r")))]
+  "TARGET_80387"
+  "*
+{
+  if (FP_REG_P (operands[0]))
+    {
+      output_movsf (operands[0], operands[1]);
+      RET;
+    }
+  if (FP_REG_P (operands[1]))
+    {
+      if (top_dead_p (insn))
+        fp_pop_df (operands[0]);
+      else
+        fp_store_df (operands[0]);
+      RET;
+    }
+  output_movsf (FP_TOP, operands[1]);
+  fp_pop_df (operands[0]);
+  RETCOM (extendsfdf2);
+}")
+
+;; This cannot output into an f-reg because there is no way to be
+;; sure of truncating in that case.
+(define_insn "truncdfsf2"
+  [(set (match_operand:SF 0 "general_operand" "=m,!*r")
+       (float_truncate:SF
+        (match_operand:DF 1 "general_operand" "f,f")))]
+  "TARGET_80387"
+  "*
+{
+  if (top_dead_p (insn))
+    fp_pop_sf (operands[0]);
+  else
+    fp_store_sf (operands[0]);
+  RETCOM (truncdfsf2);
+}")
+\f
+;; Conversion between fixed point and floating point.
+;; Note that among the fix-to-float insns
+;; the ones that start with SImode come first.
+;; That is so that an operand that is a CONST_INT
+;; (and therefore lacks a specific machine mode).
+;; will be recognized as SImode (which is always valid)
+;; rather than as QImode or HImode. The 80387 would not know
+;; what to do with the smaller sizes anyway. (I think).
+
+(define_insn "floatsisf2"
+  [(set (match_operand:SF 0 "general_operand" "=fm,fm")
+       (float:SF (match_operand:SI 1 "general_operand" "m,!*r")))]
+  "TARGET_80387"
+  "*
+{
+/*  fp_pop_level++; */
+
+  if (GET_CODE (operands[1]) != MEM)
+    {
+      rtx xops[2];
+      output_asm_insn (\"push%L0 %1\", operands);
+      operands[1] = AT_SP (SImode);
+      output_asm_insn (\"fild%L0 %1\", operands);
+      xops[0] = stack_pointer_rtx;
+      xops[1] = gen_rtx (CONST_INT, VOIDmode, 4);
+      output_asm_insn (AS2 (add%L0,%1,%0), xops);
+    }
+  else
+    output_asm_insn (\"fild%L0 %1\", operands);
+
+  if (! FP_REG_P (operands[0]))
+    {
+/*      fp_pop_level--; */
+      return \"fstp%S0 %0\";
+    }
+  RET;
+}")
+
+(define_insn "floatsidf2"
+  [(set (match_operand:DF 0 "general_operand" "=fm,fm")
+       (float:DF (match_operand:SI 1 "general_operand" "m,!*r")))]
+  "TARGET_80387"
+  "*
+{
+/*  fp_pop_level++; */
+  if (GET_CODE (operands[1]) != MEM)
+    {
+      rtx xops[2];
+      output_asm_insn (\"push%L0 %1\", operands);
+      operands[1] = AT_SP (SImode);
+      output_asm_insn (\"fild%L0 %1\", operands);
+      xops[0] = stack_pointer_rtx;
+      xops[1] = gen_rtx (CONST_INT, VOIDmode, 4);
+      output_asm_insn (AS2 (add%L0,%1,%0), xops);
+    }
+  else
+    output_asm_insn (\"fild%L0 %1\", operands);
+  if (! FP_REG_P (operands[0]))
+    {
+/*      fp_pop_level--; */
+      return \"fstp%Q0 %0\";
+    }
+  RET;
+}")
+\f
+;; Convert a float to a float whose value is an integer.
+;; This is the first stage of converting it to an integer type.
+
+;; On the 387  truncating doub to an short integer shor can be performed:
+
+;      fstcw   -4(%esp)  ;save cw
+;      movw    -4(%esp),%ax
+;      orw     $0x0c00,%ax  ;set rounding to chop towards zero
+;      movw    %ax,-2(%esp) ;
+;      fldcw   -2(%esp)     ;
+;      fldl    doubl
+;      fistpl  -12(%esp)    ;store the round value
+;      fldcw   -4(%esp)     ;restore cw
+;      movl    -12(%esp),%eax
+;      movw    %ax,shor     ; move the result into shor.
+
+;; but it is probably better to have a call, rather than waste this
+;; space.  The last instruction would have been a movl if were
+;; going to an int instead of a short.
+;; For the moment we will go with the soft float for these.
+
+/* These are incorrect since they don't set the rounding bits of CW flag.
+   The proper way to do that is to make the function prologue save the CW
+   and also construct the alternate CW value needed for these insns.
+   Then these insns can output two fldcw's, referring to fixed places in
+   the stack frame.
+
+;; Convert a float whose value is an integer
+;; to an actual integer.  Second stage of converting float to integer type.
+
+(define_insn "fix_truncsfqi2"
+  [(set (match_operand:QI 0 "general_operand" "=m,?*q")
+       (fix:QI (fix:SF (match_operand:SF 1 "general_operand" "f,f"))))]
+  "TARGET_80387"
+  "*
+{
+  fp_pop_int (operands[0]);
+  RET;
+}")
+
+(define_insn "fix_truncsfhi2"
+  [(set (match_operand:HI 0 "general_operand" "=m,?*r")
+       (fix:HI (fix:SF (match_operand:SF 1 "general_operand" "f,f"))))]
+  "TARGET_80387"
+  "*
+{
+  fp_pop_int (operands[0]);
+  RET;
+}")
+
+(define_insn "fix_truncsfsi2"
+  [(set (match_operand:SI 0 "general_operand" "=m,?*r")
+       (fix:SI (fix:SF (match_operand:SF 1 "general_operand" "f,f"))))]
+  "TARGET_80387"
+  "*
+{
+  fp_pop_int (operands[0]);
+  RET;
+}")
+
+(define_insn "fix_truncdfqi2"
+  [(set (match_operand:QI 0 "general_operand" "=m,?*q")
+       (fix:QI (fix:DF (match_operand:DF 1 "general_operand" "f,f"))))]
+
+  "TARGET_80387"
+ "*
+{
+  fp_pop_int (operands[0]);
+  RET;
+}")
+
+
+(define_insn "fix_truncdfhi2"
+  [(set (match_operand:HI 0 "general_operand" "=m,?*r")
+       (fix:HI (fix:DF (match_operand:DF 1 "general_operand" "f,f"))))]
+  "TARGET_80387"
+  "*
+{
+  fp_pop_int (operands[0]);
+  RET;
+}")
+
+
+(define_insn "fix_truncdfsi2"
+  [(set (match_operand:SI 0 "general_operand" "=m,?*r")
+       (fix:SI (fix:DF (match_operand:DF 1 "general_operand" "f,f"))))]
+  "TARGET_80387"
+  "*
+{
+  fp_pop_int (operands[0]);
+  RET;
+}")
+*/
+
+\f
+;;- add instructions
+;;moved incl to above leal
+
+(define_insn "addsi3"
+  [(set (match_operand:SI 0 "general_operand" "=rm,r")
+       (plus:SI (match_operand:SI 1 "general_operand" "%0,0")
+                (match_operand:SI 2 "general_operand" "ri,rm")))]
+  ""
+  "add%L0 %2,%0")
+
+(define_insn ""
+  [(set (match_operand:HI 0 "general_operand" "=g")
+       (plus:HI (match_operand:HI 1 "general_operand" "0")
+                (const_int 1)))]
+  ""
+  "inc%W0 %0")
+
+(define_insn "addhi3"
+  [(set (match_operand:HI 0 "general_operand" "=rm,r")
+       (plus:HI (match_operand:HI 1 "general_operand" "%0,0")
+                (match_operand:HI 2 "general_operand" "ri,rm")))]
+  ""
+  "add%W0 %2,%0")
+
+(define_insn ""
+  [(set (match_operand:QI 0 "general_operand" "=qm")
+       (plus:QI (match_operand:QI 1 "general_operand" "0")
+                (const_int 1)))]
+  ""
+  "inc%B0 %0")
+
+(define_insn "addqi3"
+  [(set (match_operand:QI 0 "general_operand" "=m,q")
+       (plus:QI (match_operand:QI 1 "general_operand" "%0,0")
+                (match_operand:QI 2 "general_operand" "qn,qmn")))]
+  ""
+  "add%B0 %2,%0")
+
+;;had "fmF,m"
+
+(define_insn "adddf3"
+  [(set (match_operand:DF 0 "general_operand" "=f,m,f")
+       (plus:DF (match_operand:DF 1 "general_operand" "%0,0,0")
+                (match_operand:DF 2 "general_operand" "m,!f,!*r")))]
+  "TARGET_80387"
+  "*FP_CALL (\"fadd%z0 %0\", \"fadd%z0 %0\", 2)")
+
+(define_insn "addsf3"
+  [(set (match_operand:SF 0 "general_operand" "=f,m,f")
+       (plus:SF (match_operand:SF 1 "general_operand" "%0,0,0")
+                (match_operand:SF 2 "general_operand" "m,!f,!*r")))]
+  "TARGET_80387"
+  "*FP_CALL (\"fadd%z0 %0\", \"fadd%z0 %0\", 2)")
+\f
+;;- subtract instructions
+
+;;moved decl above leal
+
+(define_insn "subsi3"
+  [(set (match_operand:SI 0 "general_operand" "=rm,r")
+       (minus:SI (match_operand:SI 1 "general_operand" "0,0")
+                 (match_operand:SI 2 "general_operand" "ri,rm")))]
+  ""
+  "sub%L0 %2,%0")
+
+(define_insn ""
+  [(set (match_operand:HI 0 "general_operand" "=g")
+       (minus:HI (match_operand:HI 1 "general_operand" "0")
+                 (const_int 1)))]
+  ""
+  "dec%W0 %0")
+
+(define_insn "subhi3"
+  [(set (match_operand:HI 0 "general_operand" "=rm,r")
+       (minus:HI (match_operand:HI 1 "general_operand" "0,0")
+                 (match_operand:HI 2 "general_operand" "ri,rm")))]
+  ""
+  "sub%W0 %2,%0")
+
+(define_insn ""
+  [(set (match_operand:QI 0 "general_operand" "=qm")
+       (minus:QI (match_operand:QI 1 "general_operand" "0")
+                 (const_int 1)))]
+  ""
+  "dec%B0 %0")
+
+(define_insn "subqi3"
+  [(set (match_operand:QI 0 "general_operand" "=m,q")
+       (minus:QI (match_operand:QI 1 "general_operand" "0,0")
+                 (match_operand:QI 2 "general_operand" "qn,qmn")))]
+  ""
+  "sub%B0 %2,%0")
+
+(define_insn "subdf3"
+  [(set (match_operand:DF 0 "general_operand" "=f,m,f,f")
+       (minus:DF (match_operand:DF 1 "general_operand" "0,0,0,m")
+                 (match_operand:DF 2 "general_operand" "m,!f,!*r,*0")))]
+  "TARGET_80387"
+  "*FP_CALL (\"fsub%z0 %0\", \"fsubr%z0 %0\", 2)")
+
+
+(define_insn "subsf3"
+  [(set (match_operand:SF 0 "general_operand" "=f,m,f,f")
+       (minus:SF (match_operand:SF 1 "general_operand" "0,0,0,m")
+                 (match_operand:SF 2 "general_operand" "m,!f,!*r,*0")))]
+  "TARGET_80387"
+  "*FP_CALL (\"fsub%z0 %0\", \"fsubr%z0 %0\", 2)")
+\f
+;;- multiply instructions
+
+;(define_insn "mulqi3"
+;  [(set (match_operand:QI 0 "general_operand" "=a")
+;      (mult:QI (match_operand:QI 1 "general_operand" "%0")
+;               (match_operand:QI 2 "general_operand" "qm")))]
+;  ""
+;  "mul%B0 %2,%0")
+
+(define_insn "mulhi3"
+  [(set (match_operand:HI 0 "general_operand" "=r,r")
+       (mult:SI (match_operand:HI 1 "general_operand" "%0,rm")
+                (match_operand:HI 2 "general_operand" "g,i")))]
+  ""
+  "*
+{
+  if (GET_CODE (operands[1]) == REG
+      && REGNO (operands[1]) == REGNO (operands[0])
+      && (GET_CODE (operands[2]) == MEM
+         || GET_CODE (operands[2]) == REG))
+    /* Assembler has weird restrictions.  */
+    return AS2 (imul%W0,%2,%0);
+  return AS3 (imul%W0,%2,%1,%0);
+}")
+
+(define_insn "mulsi3"
+  [(set (match_operand:SI 0 "general_operand" "=r,r")
+       (mult:SI (match_operand:SI 1 "general_operand" "%0,rm")
+                (match_operand:SI 2 "general_operand" "g,i")))]
+  ""
+  "*
+{
+  if (GET_CODE (operands[1]) == REG
+      && REGNO (operands[1]) == REGNO (operands[0])
+      && (GET_CODE (operands[2]) == MEM
+         || GET_CODE (operands[2]) == REG))
+    /* Assembler has weird restrictions.  */
+    return AS2 (imul%L0,%2,%0);
+  return AS3 (imul%L0,%2,%1,%0);
+}")
+
+;; Turned off due to possible assembler bug.
+;(define_insn "umulqi3"
+;  [(set (match_operand:QI 0 "general_operand" "=a")
+;      (umult:QI (match_operand:QI 1 "general_operand" "%0")
+;                (match_operand:QI 2 "general_operand" "qm")))]
+;  ""
+;  "mul%B0 %2,%0")
+
+;(define_insn "umulqihi3"
+;  [(set (match_operand:HI 0 "general_operand" "=a")
+;      (umult:HI (match_operand:QI 1 "general_operand" "%0")
+;                (match_operand:QI 2 "general_operand" "qm")))]
+;  ""
+;  "mul%B0 %2,%0")
+
+(define_insn "umulhi3"
+  [(set (match_operand:HI 0 "general_operand" "=a")
+       (umult:SI (match_operand:HI 1 "general_operand" "%0")
+                 (match_operand:HI 2 "general_operand" "rm")))
+   (clobber (reg:HI 1))]
+  ""
+  "mul%W0 %2,%0")
+
+(define_insn "umulsi3"
+  [(set (match_operand:SI 0 "general_operand" "=a")
+       (umult:SI (match_operand:SI 1 "general_operand" "%0")
+                 (match_operand:SI 2 "general_operand" "rm")))
+   (clobber (reg:SI 1))]
+  ""
+  "mul%L0 %2,%0")
+
+(define_insn "muldf3"
+  [(set (match_operand:DF 0 "general_operand" "=f,m,f")
+       (mult:DF (match_operand:DF 1 "general_operand" "%0,0,0")
+                (match_operand:DF 2 "general_operand" "m,!f,!*r")))]
+  "TARGET_80387"
+   "*FP_CALL (\"fmul%z0 %0\", \"fmul%z0 %0\", 2)
+")
+
+(define_insn "mulsf3"
+  [(set (match_operand:SF 0 "general_operand" "=f,m,f")
+       (mult:SF (match_operand:SF 1 "general_operand" "%0,0,0")
+                (match_operand:SF 2 "general_operand" "m,!f,!*r")))]
+  "TARGET_80387"
+  "*FP_CALL (\"fmul%z0 %0\", \"fmul%z0 %0\", 2)
+")
+\f
+;;- divide instructions
+(define_insn "divdf3"
+  [(set (match_operand:DF 0 "general_operand" "=f,m,f,f")
+       (div:DF (match_operand:DF 1 "general_operand" "0,0,0,m")
+               (match_operand:DF 2 "general_operand" "m,!f,!*r,*0")))]
+  "TARGET_80387"
+  "*FP_CALL (\"fdiv%z0 %0\", \"fdivr%z0 %0\", 2)
+")
+
+(define_insn "divsf3"
+  [(set (match_operand:SF 0 "general_operand" "=f,m,f,f")
+       (div:SF (match_operand:SF 1 "general_operand" "0,0,0,m")
+               (match_operand:SF 2 "general_operand" "m,!f,!*r,*0")))]
+  "TARGET_80387"
+  "*FP_CALL (\"fdiv%z0 %0\", \"fdivr%z0 %0\", 2)
+")
+\f
+;; Remainder instructions.
+
+(define_insn "divmodsi4"
+  [(set (match_operand:SI 0 "general_operand" "=a")
+       (div:SI (match_operand:SI 1 "general_operand" "0")
+               (match_operand:SI 2 "general_operand" "rm")))
+   (set (match_operand:SI 3 "general_operand" "=&d")
+       (mod:SI (match_dup 1) (match_dup 2)))]
+  ""
+  "cltd\;idiv%L0 %2")
+
+(define_insn "udivmodsi4"
+  [(set (match_operand:SI 0 "general_operand" "=a")
+       (udiv:SI (match_operand:SI 1 "general_operand" "0")
+               (match_operand:SI 2 "general_operand" "rm")))
+   (set (match_operand:SI 3 "general_operand" "=&d")
+       (umod:SI (match_dup 1) (match_dup 2)))]
+  ""
+  "xor%L0 %3,%3\;div%L0 %2")
+
+/*
+;;this should be a valid double division which we may want to add
+
+(define_insn ""
+  [(set (match_operand:SI 0 "general_operand" "=a")
+       (udiv:DI (match_operand:DI 1 "general_operand" "a")
+               (match_operand:SI 2 "general_operand" "rm")))
+   (set (match_operand:SI 3 "general_operand" "=d")
+       (umod:SI (match_dup 1) (match_dup 2)))]
+  ""
+  "div%L0 %2,%0")
+*/
+\f
+;;- and instructions
+
+;; The `r' in `rm' for operand 3 looks redundant, but it causes
+;; optional reloads to be generated if op 3 is a pseudo in a stack slot.
+
+(define_insn "andsi3"
+  [(set (match_operand:SI 0 "general_operand" "=rm,r")
+       (and:SI (match_operand:SI 1 "general_operand" "%0,0")
+               (match_operand:SI 2 "general_operand" "ri,rm")))]
+  ""
+  "and%L0 %2,%0")
+
+(define_insn "andhi3"
+  [(set (match_operand:HI 0 "general_operand" "=rm,r")
+       (and:HI (match_operand:HI 1 "general_operand" "%0,0")
+               (match_operand:HI 2 "general_operand" "ri,rm")))]
+  ""
+  "and%W0 %2,%0")
+
+(define_insn "andqi3"
+  [(set (match_operand:QI 0 "general_operand" "=m,q")
+       (and:QI (match_operand:QI 1 "general_operand" "%0,0")
+               (match_operand:QI 2 "general_operand" "qn,qmn")))]
+  ""
+  "and%B0 %2,%0")
+
+/* I am nervous about these two.. add them later..
+;I presume this means that we have something in say op0= eax which is small
+;and we want to and it with memory so we can do this by just an
+;andb m,%al  and have success.
+(define_insn ""
+  [(set (match_operand:SI 0 "general_operand" "=r")
+       (and:SI (zero_extend:SI (match_operand:HI 1 "general_operand" "rm"))
+               (match_operand:SI 2 "general_operand" "0")))]
+  "GET_CODE (operands[2]) == CONST_INT
+   && (unsigned int) INTVAL (operands[2]) < (1 << GET_MODE_BITSIZE (HImode))"
+  "and%W0 %1,%0")
+
+(define_insn ""
+  [(set (match_operand:SI 0 "general_operand" "=q")
+       (and:SI (zero_extend:SI (match_operand:QI 1 "general_operand" "qm"))
+               (match_operand:SI 2 "general_operand" "0")))]
+  "GET_CODE (operands[2]) == CONST_INT
+   && (unsigned int) INTVAL (operands[2]) < (1 << GET_MODE_BITSIZE (QImode))"
+  "and%L0 %1,%0")
+
+*/
+
+
+\f
+;;- Bit set (inclusive or) instructions
+
+(define_insn "iorsi3"
+  [(set (match_operand:SI 0 "general_operand" "=rm,r")
+       (ior:SI (match_operand:SI 1 "general_operand" "%0,0")
+               (match_operand:SI 2 "general_operand" "ri,rm")))]
+  ""
+  "or%L0 %2,%0")
+
+(define_insn "iorhi3"
+  [(set (match_operand:HI 0 "general_operand" "=rm,r")
+       (ior:HI (match_operand:HI 1 "general_operand" "%0,0")
+               (match_operand:HI 2 "general_operand" "ri,rm")))]
+  ""
+  "or%W0 %2,%0")
+
+(define_insn "iorqi3"
+  [(set (match_operand:QI 0 "general_operand" "=m,q")
+       (ior:QI (match_operand:QI 1 "general_operand" "%0,0")
+               (match_operand:QI 2 "general_operand" "qn,qmn")))]
+  ""
+  "or%B0 %2,%0")
+\f
+;;- xor instructions
+
+(define_insn "xorsi3"
+  [(set (match_operand:SI 0 "general_operand" "=rm,r")
+       (xor:SI (match_operand:SI 1 "general_operand" "%0,0")
+               (match_operand:SI 2 "general_operand" "ri,rm")))]
+  ""
+  "xor%L0 %2,%0")
+
+(define_insn "xorhi3"
+  [(set (match_operand:HI 0 "general_operand" "=rm,r")
+       (xor:HI (match_operand:HI 1 "general_operand" "%0,0")
+               (match_operand:HI 2 "general_operand" "ri,rm")))]
+  ""
+  "xor%W0 %2,%0")
+
+(define_insn "xorqi3"
+  [(set (match_operand:QI 0 "general_operand" "=qm")
+       (xor:QI (match_operand:QI 1 "general_operand" "%0")
+               (match_operand:QI 2 "general_operand" "qn")))]
+  ""
+  "xor%B0 %2,%0")
+\f
+;;- negation instructions
+(define_insn "negsi2"
+  [(set (match_operand:SI 0 "general_operand" "=rm")
+       (neg:SI (match_operand:SI 1 "general_operand" "0")))]
+  ""
+  "neg%L0 %0")
+
+(define_insn "neghi2"
+  [(set (match_operand:HI 0 "general_operand" "=rm")
+       (neg:HI (match_operand:HI 1 "general_operand" "0")))]
+  ""
+  "neg%W0 %0")
+
+(define_insn "negqi2"
+  [(set (match_operand:QI 0 "general_operand" "=qm")
+       (neg:QI (match_operand:QI 1 "general_operand" "0")))]
+  ""
+  "neg%B0 %0")
+
+(define_insn "negsf2"
+  [(set (match_operand:SF 0 "general_operand" "=f,!m")
+       (neg:SF (match_operand:SF 1 "general_operand" "0,0")))]
+  "TARGET_80387"
+  "*FP_CALL1 (\"fchs\")")
+
+(define_insn "negdf2"
+  [(set (match_operand:DF 0 "general_operand" "=f,!m")
+       (neg:DF (match_operand:DF 1 "general_operand" "0,0")))]
+  "TARGET_80387"
+  "*FP_CALL1 (\"fchs\")")
+\f
+;; Absolute value instructions
+
+(define_insn "abssf2"
+  [(set (match_operand:SF 0 "general_operand" "=f,!m")
+       (abs:SF (match_operand:SF 1 "general_operand" "0,0")))]
+  "TARGET_80387"
+  "*FP_CALL1 (\"fabs\")")
+
+(define_insn "absdf2"
+  [(set (match_operand:DF 0 "general_operand" "=f,!m")
+       (abs:DF (match_operand:DF 1 "general_operand" "0,0")))]
+  "TARGET_80387"
+  "*FP_CALL1 (\"fabs\")")
+\f
+;;- one complement instructions
+(define_insn "one_cmplsi2"
+  [(set (match_operand:SI 0 "general_operand" "=rm")
+       (not:SI (match_operand:SI 1 "general_operand" "0")))]
+  ""
+  "not%L0 %0")
+
+(define_insn "one_cmplhi2"
+  [(set (match_operand:HI 0 "general_operand" "=rm")
+       (not:HI (match_operand:HI 1 "general_operand" "0")))]
+  ""
+  "not%W0 %0")
+
+(define_insn "one_cmplqi2"
+  [(set (match_operand:QI 0 "general_operand" "=qm")
+       (not:QI (match_operand:QI 1 "general_operand" "0")))]
+  ""
+  "not%B0 %0")
+\f
+;;- arithmetic shift instructions
+
+(define_insn "ashlsi3"
+  [(set (match_operand:SI 0 "general_operand" "=rm")
+       (ashift:SI (match_operand:SI 1 "general_operand" "0")
+                  (match_operand:SI 2 "general_operand" "cI")))]
+  ""
+  "*
+{
+  if (REG_P (operands[2]))
+    return AS2 (sal%L0,%R0cl,%0);
+  else if (REG_P (operands[1]) && GET_CODE (operands[2]) == CONST_INT
+          && INTVAL (operands[2]) == 1)
+    return AS2 (add%L0,%1,%1);
+  return AS2 (sal%L0,%2,%1);
+}")
+
+(define_insn "ashlhi3"
+  [(set (match_operand:HI 0 "general_operand" "=rm")
+       (ashift:HI (match_operand:HI 1 "general_operand" "0")
+                  (match_operand:HI 2 "general_operand" "cI")))]
+  ""
+  "*
+{
+  if (REG_P (operands[2]))
+    return AS2 (sal%W0,%R0cl,%0);
+  else
+    return AS2 (sal%W0,%2,%1);
+}")
+
+(define_insn "ashlqi3"
+  [(set (match_operand:QI 0 "general_operand" "=qm")
+       (ashift:QI (match_operand:QI 1 "general_operand" "0")
+                  (match_operand:QI 2 "general_operand" "cI")))]
+  ""
+  "*
+{
+  if (REG_P (operands[2]))
+    return AS2 (sal%B0,%R0cl,%0);
+  else
+    return AS2 (sal%B0,%2,%1);
+}")
+
+(define_insn "ashrsi3"
+  [(set (match_operand:SI 0 "general_operand" "=rm")
+       (ashiftrt:SI (match_operand:SI 1 "general_operand" "0")
+                    (match_operand:SI 2 "general_operand" "cI")))]
+  ""
+  "*
+{
+  if (REG_P (operands[2]))
+    return AS2 (sar%L0,%R0cl,%0);
+  else
+    return AS2 (sar%L0,%2,%0);
+}")
+
+(define_insn "ashrhi3"
+  [(set (match_operand:HI 0 "general_operand" "=rm")
+       (ashiftrt:HI (match_operand:HI 1 "general_operand" "0")
+                    (match_operand:HI 2 "general_operand" "cI")))]
+  ""
+  "*
+{
+  if (REG_P (operands[2]))
+    return AS2 (sar%W0,%R0cl,%0);
+  else
+    return AS2 (sar%W0,%2,%0);
+}")
+
+(define_insn "ashrqi3"
+  [(set (match_operand:QI 0 "general_operand" "=qm")
+       (ashiftrt:QI (match_operand:QI 1 "general_operand" "0")
+                    (match_operand:QI 2 "general_operand" "cI")))]
+  ""
+  "*
+{
+  if (REG_P (operands[2]))
+    return AS2 (sar%B0,%R0cl,%0);
+  return
+    AS2 (sar%B0,%2,%1);
+}")
+\f
+;;- logical shift instructions
+
+(define_insn "lshlsi3"
+  [(set (match_operand:SI 0 "general_operand" "=rm")
+       (lshift:SI (match_operand:SI 1 "general_operand" "0")
+                  (match_operand:SI 2 "general_operand" "cI")))]
+  ""
+  "*
+{
+  if (REG_P (operands[2]))
+    return AS2 (shl%L0,%R0cl,%0);
+  else
+    return AS2 (shl%L0,%2,%1);
+}")
+
+(define_insn "lshlhi3"
+  [(set (match_operand:HI 0 "general_operand" "=rm")
+       (lshift:HI (match_operand:HI 1 "general_operand" "0")
+                  (match_operand:HI 2 "general_operand" "cI")))]
+  ""
+  "*
+{
+  if (REG_P (operands[2]))
+    return AS2 (shl%W0,%R0cl,%0);
+  else
+    return AS2 (shl%W0,%2,%1);
+}")
+
+(define_insn "lshlqi3"
+  [(set (match_operand:QI 0 "general_operand" "=qm")
+       (lshift:QI (match_operand:QI 1 "general_operand" "0")
+                  (match_operand:QI 2 "general_operand" "cI")))]
+  ""
+  "*
+{
+  if (REG_P (operands[2]))
+    return AS2 (shl%B0,%R0cl,%0);
+  else
+    return AS2 (shl%B0,%2,%1);
+}")
+
+(define_insn "lshrsi3"
+  [(set (match_operand:SI 0 "general_operand" "=rm")
+       (lshiftrt:SI (match_operand:SI 1 "general_operand" "0")
+                    (match_operand:SI 2 "general_operand" "cI")))]
+  ""
+  "*
+{
+  if (REG_P (operands[2]))
+    return AS2 (shr%L0,%R0cl,%0);
+  else
+    return AS2 (shr%L0,%2,%1);
+}")
+
+(define_insn "lshrhi3"
+  [(set (match_operand:HI 0 "general_operand" "=rm")
+       (lshiftrt:HI (match_operand:HI 1 "general_operand" "0")
+                    (match_operand:HI 2 "general_operand" "cI")))]
+  ""
+  "*
+{
+  if (REG_P (operands[2]))
+    return AS2 (shr%W0,%%cl,%0);
+  else
+    return AS2 (shr%W0,%2,%1);
+}")
+
+(define_insn "lshrqi3"
+  [(set (match_operand:QI 0 "general_operand" "=qm")
+       (lshiftrt:QI (match_operand:QI 1 "general_operand" "0")
+                    (match_operand:QI 2 "general_operand" "cI")))]
+  ""
+  "*
+{
+  if (REG_P (operands[2]))
+    return AS2 (shr%B0,%%cl,%0);
+  else
+    return AS2 (shr%B0,%2,%1);
+}")
+\f
+;;- rotate instructions
+
+(define_insn "rotlsi3"
+  [(set (match_operand:SI 0 "general_operand" "=rm")
+       (rotate:SI (match_operand:SI 1 "general_operand" "0")
+                  (match_operand:SI 2 "general_operand" "cI")))]
+  ""
+  "*
+{
+  if (REG_P (operands[2]))
+    return AS2 (rol%L0,%%cl,%0);
+  else
+    return AS2 (rol%L0,%2,%1);
+}")
+
+(define_insn "rotlhi3"
+  [(set (match_operand:HI 0 "general_operand" "=rm")
+       (rotate:HI (match_operand:HI 1 "general_operand" "0")
+                  (match_operand:HI 2 "general_operand" "cI")))]
+  ""
+  "*
+{
+  if (REG_P (operands[2]))
+    return AS2 (rol%W0,%%cl,%0);
+  else
+    return AS2 (rol%W0,%2,%1);
+}")
+
+(define_insn "rotlqi3"
+  [(set (match_operand:QI 0 "general_operand" "=qm")
+       (rotate:QI (match_operand:QI 1 "general_operand" "0")
+                  (match_operand:QI 2 "general_operand" "cI")))]
+  ""
+  "*
+{
+  if (REG_P (operands[2]))
+    return AS2 (rol%B0,%%cl,%0);
+  else
+    return AS2 (rol%B0,%2,%1);
+}")
+
+(define_insn "rotrsi3"
+  [(set (match_operand:SI 0 "general_operand" "=rm")
+       (rotatert:SI (match_operand:SI 1 "general_operand" "0")
+                    (match_operand:SI 2 "general_operand" "cI")))]
+  ""
+  "*
+{
+  if (REG_P (operands[2]))
+    return AS2 (ror%L0,%%cl,%0);
+  else
+    return AS2 (ror%L0,%2,%1);
+}")
+
+(define_insn "rotrhi3"
+  [(set (match_operand:HI 0 "general_operand" "=rm")
+       (rotatert:HI (match_operand:HI 1 "general_operand" "0")
+                    (match_operand:HI 2 "general_operand" "cI")))]
+  ""
+  "*
+{
+  if (REG_P (operands[2]))
+    return AS2 (ror%W0,%%cl,%0);
+  else
+    return AS2 (ror%W0,%2,%1);
+}")
+
+(define_insn "rotrqi3"
+  [(set (match_operand:QI 0 "general_operand" "=qm")
+       (rotatert:QI (match_operand:QI 1 "general_operand" "0")
+                    (match_operand:QI 2 "general_operand" "cI")))]
+  ""
+  "*
+{
+  if (REG_P (operands[2]))
+    return AS2 (ror%B0,%%cl,%0);
+  else
+    return AS2 (ror%B0,%2,%1);
+}")
+\f
+;; Store-flag instructions.
+
+(define_insn "seq"
+  [(set (match_operand:QI 0 "general_operand" "=q")
+       (eq (cc0) (const_int 0)))]
+  ""
+  "*
+  cc_status = cc_prev_status;
+  return \"sete %0\";
+")
+
+(define_insn "sne"
+  [(set (match_operand:QI 0 "general_operand" "=q")
+       (ne (cc0) (const_int 0)))]
+  ""
+  "*
+  cc_status = cc_prev_status;
+  return \"setne %0\";
+")
+
+(define_insn "sgt"
+  [(set (match_operand:QI 0 "general_operand" "=q")
+       (gt (cc0) (const_int 0)))]
+  ""
+  "*
+  cc_status = cc_prev_status;
+  OUTPUT_JUMP (\"setg %0\", \"seta %0\", 0);
+")
+
+(define_insn "sgtu"
+  [(set (match_operand:QI 0 "general_operand" "=q")
+       (gtu (cc0) (const_int 0)))]
+  ""
+  "* cc_status = cc_prev_status;
+     return \"seta %0\"; ")
+
+(define_insn "slt"
+  [(set (match_operand:QI 0 "general_operand" "=q")
+       (lt (cc0) (const_int 0)))]
+  ""
+  "* cc_status = cc_prev_status;
+     OUTPUT_JUMP (\"setl %0\", \"setb %0\", \"sets %0\"); ")
+
+(define_insn "sltu"
+  [(set (match_operand:QI 0 "general_operand" "=q")
+       (ltu (cc0) (const_int 0)))]
+  ""
+  "* cc_status = cc_prev_status;
+     return \"setb %0\"; ")
+
+(define_insn "sge"
+  [(set (match_operand:QI 0 "general_operand" "=q")
+       (ge (cc0) (const_int 0)))]
+  ""
+  "* cc_status = cc_prev_status;
+     OUTPUT_JUMP (\"setge %0\", \"setae %0\", \"setns %0\"); ")
+
+(define_insn "sgeu"
+  [(set (match_operand:QI 0 "general_operand" "=q")
+       (geu (cc0) (const_int 0)))]
+  ""
+  "* cc_status = cc_prev_status;
+     return \"setae %0\"; ")
+
+(define_insn "sle"
+  [(set (match_operand:QI 0 "general_operand" "=q")
+       (le (cc0) (const_int 0)))]
+  ""
+  "*
+  cc_status = cc_prev_status;
+  OUTPUT_JUMP (\"setle %0\", \"setbe %0\", 0);
+")
+
+(define_insn "sleu"
+  [(set (match_operand:QI 0 "general_operand" "=q")
+       (leu (cc0) (const_int 0)))]
+  ""
+  "* cc_status = cc_prev_status;
+     return \"setbe %0\"; ")
+\f
+;; Basic conditional jump instructions.
+;; We ignore the overflow flag for signed branch instructions.
+
+(define_insn "beq"
+  [(set (pc)
+       (if_then_else (eq (cc0)
+                         (const_int 0))
+                     (label_ref (match_operand 0 "" ""))
+                     (pc)))]
+  ""
+  "je %l0")
+
+(define_insn "bne"
+  [(set (pc)
+       (if_then_else (ne (cc0)
+                         (const_int 0))
+                     (label_ref (match_operand 0 "" ""))
+                     (pc)))]
+  ""
+  "jne %l0")
+
+(define_insn "bgt"
+  [(set (pc)
+       (if_then_else (gt (cc0)
+                         (const_int 0))
+                     (label_ref (match_operand 0 "" ""))
+                     (pc)))]
+  ""
+  "*OUTPUT_JUMP (\"jg %l0\", \"ja %l0\", 0)")
+
+(define_insn "bgtu"
+  [(set (pc)
+       (if_then_else (gtu (cc0)
+                          (const_int 0))
+                     (label_ref (match_operand 0 "" ""))
+                     (pc)))]
+  ""
+  "ja %l0")
+
+;; There is no jump insn to check for `<' on IEEE floats.
+;; Page 17-80 in the 80387 manual says jb, but that's wrong;
+;; jb checks for `not >='.  So swap the operands and do `>'.
+(define_expand "blt"
+  [(set (pc)
+       (if_then_else (lt (cc0)
+                         (const_int 0))
+                     (label_ref (match_operand 0 "" ""))
+                     (pc)))]
+  ""
+  "
+{
+  extern rtx sequence_stack;
+  rtx prev = XEXP (XEXP (sequence_stack, 1), 0);
+  rtx body = PATTERN (prev);
+  rtx comp;
+  if (GET_CODE (body) == SET)
+    comp = SET_SRC (body);
+  else
+    comp = SET_SRC (XVECEXP (body, 0, 0));
+
+  if (GET_CODE (comp) == COMPARE
+      ? GET_MODE_CLASS (GET_MODE (XEXP (comp, 0))) == MODE_FLOAT
+      : GET_MODE_CLASS (GET_MODE (comp)) == MODE_FLOAT)
+    {
+      if (GET_CODE (comp) == COMPARE)
+       {
+         rtx op0 = XEXP (comp, 0);
+         rtx op1 = XEXP (comp, 1);
+         XEXP (comp, 0) = op1;
+         XEXP (comp, 1) = op0;
+       }
+      else
+       {
+         rtx new = gen_rtx (COMPARE, VOIDmode,
+                            CONST0_RTX (GET_MODE (comp)), comp);
+         if (GET_CODE (body) == SET)
+           SET_SRC (body) = new;
+         else
+           SET_SRC (XVECEXP (body, 0, 0)) = new;
+       }
+      emit_insn (gen_bgt (operands[0]));
+      DONE;
+    }
+}")
+
+(define_insn ""
+  [(set (pc)
+       (if_then_else (lt (cc0)
+                         (const_int 0))
+                     (label_ref (match_operand 0 "" ""))
+                     (pc)))]
+  ""
+  "*OUTPUT_JUMP (\"jl %l0\", \"jb %l0\", \"js %l0\")")
+
+(define_insn "bltu"
+  [(set (pc)
+       (if_then_else (ltu (cc0)
+                          (const_int 0))
+                     (label_ref (match_operand 0 "" ""))
+                     (pc)))]
+  ""
+  "jb %l0")
+
+(define_insn "bge"
+  [(set (pc)
+       (if_then_else (ge (cc0)
+                         (const_int 0))
+                     (label_ref (match_operand 0 "" ""))
+                     (pc)))]
+  ""
+  "*OUTPUT_JUMP (\"jge %l0\", \"jae %l0\", \"jns %l0\")")
+
+(define_insn "bgeu"
+  [(set (pc)
+       (if_then_else (geu (cc0)
+                          (const_int 0))
+                     (label_ref (match_operand 0 "" ""))
+                     (pc)))]
+  ""
+  "jae %l0")
+
+;; See comment on `blt', above.
+(define_expand "ble"
+  [(set (pc)
+       (if_then_else (le (cc0)
+                         (const_int 0))
+                     (label_ref (match_operand 0 "" ""))
+                     (pc)))]
+  ""
+  "
+{
+  extern rtx sequence_stack;
+  rtx prev = XEXP (XEXP (sequence_stack, 1), 0);
+  rtx body = PATTERN (prev);
+  rtx comp;
+  if (GET_CODE (body) == SET)
+    comp = SET_SRC (body);
+  else
+    comp = SET_SRC (XVECEXP (body, 0, 0));
+
+  if (GET_CODE (comp) == COMPARE
+      ? GET_MODE_CLASS (GET_MODE (XEXP (comp, 0))) == MODE_FLOAT
+      : GET_MODE_CLASS (GET_MODE (comp)) == MODE_FLOAT)
+    {
+      if (GET_CODE (comp) == COMPARE)
+       {
+         rtx op0 = XEXP (comp, 0);
+         rtx op1 = XEXP (comp, 1);
+         XEXP (comp, 0) = op1;
+         XEXP (comp, 1) = op0;
+       }
+      else
+       {
+         rtx new = gen_rtx (COMPARE, VOIDmode,
+                            CONST0_RTX (GET_MODE (comp)), comp);
+         if (GET_CODE (body) == SET)
+           SET_SRC (body) = new;
+         else
+           SET_SRC (XVECEXP (body, 0, 0)) = new;
+       }
+      emit_insn (gen_bge (operands[0]));
+      DONE;
+    }
+}")
+
+(define_insn ""
+  [(set (pc)
+       (if_then_else (le (cc0)
+                         (const_int 0))
+                     (label_ref (match_operand 0 "" ""))
+                     (pc)))]
+  ""
+  "*OUTPUT_JUMP (\"jle %l0\", \"jbe %l0\", 0) ")
+
+(define_insn "bleu"
+  [(set (pc)
+       (if_then_else (leu (cc0)
+                          (const_int 0))
+                     (label_ref (match_operand 0 "" ""))
+                     (pc)))]
+  ""
+  "jbe %l0")
+\f
+;; Negated conditional jump instructions.
+
+(define_insn ""
+  [(set (pc)
+       (if_then_else (eq (cc0)
+                         (const_int 0))
+                     (pc)
+                     (label_ref (match_operand 0 "" ""))))]
+  ""
+  "jne %l0")
+
+(define_insn ""
+  [(set (pc)
+       (if_then_else (ne (cc0)
+                         (const_int 0))
+                     (pc)
+                     (label_ref (match_operand 0 "" ""))))]
+  ""
+  "je %l0")
+
+(define_insn ""
+  [(set (pc)
+       (if_then_else (gt (cc0)
+                         (const_int 0))
+                     (pc)
+                     (label_ref (match_operand 0 "" ""))))]
+  ""
+  "*OUTPUT_JUMP (\"jle %l0\", \"jbe %l0\", 0) ")
+
+(define_insn ""
+  [(set (pc)
+       (if_then_else (gtu (cc0)
+                          (const_int 0))
+                     (pc)
+                     (label_ref (match_operand 0 "" ""))))]
+  ""
+  "jbe %l0")
+
+(define_insn ""
+  [(set (pc)
+       (if_then_else (lt (cc0)
+                         (const_int 0))
+                     (pc)
+                     (label_ref (match_operand 0 "" ""))))]
+  ""
+  "*OUTPUT_JUMP (\"jge %l0\", \"jae %l0\", \"jns %l0\")
+")
+
+(define_insn ""
+  [(set (pc)
+       (if_then_else (ltu (cc0)
+                          (const_int 0))
+                     (pc)
+                     (label_ref (match_operand 0 "" ""))))]
+  ""
+  "jae %l0")
+
+(define_insn ""
+  [(set (pc)
+       (if_then_else (ge (cc0)
+                         (const_int 0))
+                     (pc)
+                     (label_ref (match_operand 0 "" ""))))]
+  ""
+  "*OUTPUT_JUMP (\"jl %l0\", \"jb %l0\", \"js %l0\")")
+
+(define_insn ""
+  [(set (pc)
+       (if_then_else (geu (cc0)
+                          (const_int 0))
+                     (pc)
+                     (label_ref (match_operand 0 "" ""))))]
+  ""
+  "jb %l0")
+
+(define_insn ""
+  [(set (pc)
+       (if_then_else (le (cc0)
+                         (const_int 0))
+                     (pc)
+                     (label_ref (match_operand 0 "" ""))))]
+  ""
+  "*OUTPUT_JUMP (\"jg %l0\", \"ja %l0\", 0)")
+
+(define_insn ""
+  [(set (pc)
+       (if_then_else (leu (cc0)
+                          (const_int 0))
+                     (pc)
+                     (label_ref (match_operand 0 "" ""))))]
+  ""
+  "ja %l0")
+\f
+;; Unconditional and other jump instructions
+(define_insn "jump"
+  [(set (pc)
+       (label_ref (match_operand 0 "" "")))]
+  ""
+  "jmp %l0")
+
+(define_insn "tablejump"
+  [(set (pc) (match_operand:SI 0 "general_operand" "rm"))
+   (use (label_ref (match_operand 1 "" "")))]
+  ""
+  "*
+{
+  CC_STATUS_INIT;
+
+  return \"jmp %*%0\";
+}")
+
+/*
+(define_insn ""
+  [(set (pc)
+       (if_then_else
+        (ne (compare (minus:HI (match_operand:HI 0 "general_operand" "c")
+                               (const_int 1))
+                     (const_int -1))
+            (const_int 0))
+        (label_ref (match_operand 1 "" "g"))
+        (pc)))
+   (set (match_dup 0)
+       (minus:HI (match_dup 0)
+                 (const_int 1)))]
+  ""
+  "loop %l1")
+
+(define_insn ""
+  [(set (pc)
+       (if_then_else
+        (ne (compare (const_int -1)
+                     (minus:SI (match_operand:SI 0 "general_operand" "c")
+                               (const_int 1)))
+            (const_int 0))
+        (label_ref (match_operand 1 "" "g"))
+        (pc)))
+   (set (match_dup 0)
+       (minus:SI (match_dup 0)
+                 (const_int 1)))]
+  ""
+  "loop %l1")
+*/
+
+;; Call subroutine returning no value.
+(define_insn "call"
+  [(call (match_operand:QI 0 "indirect_operand" "m")
+        (match_operand:SI 1 "general_operand" "g"))]
+  ;; Operand 1 not really used on the m68000.
+  ""
+  "*
+{
+  if (GET_CODE (operands[0]) == MEM
+      && ! CONSTANT_ADDRESS_P (XEXP (operands[0], 0)))
+    {
+      operands[0] = XEXP (operands[0], 0);
+      return \"call %*%0\";
+    }
+  else
+    return \"call %0\";
+}")
+
+;; Call subroutine, returning value in operand 0
+;; (which must be a hard register).
+(define_insn "call_value"
+  [(set (match_operand 0 "" "=rf")
+       (call (match_operand:QI 1 "indirect_operand" "m")
+             (match_operand:SI 2 "general_operand" "g")))]
+  ;; Operand 2 not really used on the m68000.
+  ""
+  "*
+{
+  if (GET_CODE (operands[1]) == MEM
+      && ! CONSTANT_ADDRESS_P (XEXP (operands[1], 0)))
+    {
+      operands[1] = XEXP (operands[1], 0);
+      output_asm_insn (\"call %*%1\", operands);
+    }
+  else
+    output_asm_insn (\"call %1\", operands);
+
+  if (GET_MODE (operands[0]) == DFmode
+      || GET_MODE (operands[0]) == SFmode)
+    {
+/*      fp_pop_level++; */
+      /* pop if reg dead */
+      if (!FP_REG_P (operands[0]))
+       abort ();
+      if (top_dead_p (insn))
+       {
+         POP_ONE_FP;
+       }
+    }
+  RET;
+}")
+
+(define_insn "nop"
+  [(const_int 0)]
+  ""
+  "nop")
+\f
+;;- Local variables:
+;;- mode:emacs-lisp
+;;- comment-start: ";;- "
+;;- eval: (set-syntax-table (copy-sequence (syntax-table)))
+;;- eval: (modify-syntax-entry ?[ "(]")
+;;- eval: (modify-syntax-entry ?] ")[")
+;;- eval: (modify-syntax-entry ?{ "(}")
+;;- eval: (modify-syntax-entry ?} "){")
+;;- End:
diff --git a/usr/src/usr.bin/gcc/cc1/config/out-i386.c b/usr/src/usr.bin/gcc/cc1/config/out-i386.c
new file mode 100644 (file)
index 0000000..65540ec
--- /dev/null
@@ -0,0 +1,1427 @@
+/*-
+ *
+ * This code is derived from software copyrighted by the Free Software
+ * Foundation.
+ *
+ * Modified 1991 by Donn Seeley at UUNET Technologies, Inc.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)out-i386.c 6.4 (Berkeley) 5/8/91";
+#endif /* not lint */
+
+/* Subroutines for insn-output.c for Intel 80386.
+   Copyright (C) 1988 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 1, 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.  */
+
+#ifndef FILE
+#include <stdio.h>
+#endif
+
+#define FP_TOP (gen_rtx(REG, DFmode, FIRST_FLOAT_REG))
+
+#define AT_SP(mode) (gen_rtx (MEM, (mode), stack_pointer_rtx))
+#define AT_BP(mode) (gen_rtx (MEM, (mode), frame_pointer_rtx))
+
+#define RET return ""
+
+/* #define RETCOM(X) fprintf (asm_out_file, "%sX fp_pop_level=%d\n", \
+                          COMMENT_BEGIN, fp_pop_level); RET */
+#define RETCOM(X) return ""
+
+#define POP_ONE_FP     \
+  { /* fp_pop_level--; */      \
+    fprintf (asm_out_file, "\tfstp %sst(0)\n", RP); }
+
+extern FILE *asm_out_file;
+static char *singlemove_string ();
+static void output_movf ();
+static void replace_float_constant ();
+static int mentions_fp_top ();
+static int call_top_dead_p ();
+static int fp_top_dead_p1 ();
+static rtx via_memory ();
+static void output_asm_insn_double_reg_op ();
+
+/* All output functions must increment or decrement this to indicate
+   the net number of pops or pushes which they perform.  Note that it won't
+   necessarily balance with the optimize running, since we might have
+   two different calls with the same pop shared by cross jumping.
+   However on optimize the reg dead heuristic seems to work.  */
+
+int fp_pop_level = 0;
+
+static char *hi_reg_name[] = HI_REGISTER_NAMES;
+static char *qi_reg_name[] = QI_REGISTER_NAMES;
+\f
+/* for fabs, fch, .. where the argument operand[1] must first be moved to
+  constraints  "=fm" "0" */
+
+#define FP_CALL1(op)  \
+  { if (FP_REG_P (operands[0]))                \
+      return op;                       \
+    output_movf (FP_TOP, operands[1]); \
+    output_asm_insn (op, operands);    \
+    /* fp_pop_level--; */              \
+    return "fstp%z0 %0"; }
+
+/* handle case of call where op0/op1 is "=mf" and opn is "mrf"
+   eg. fadd */
+#define FP_CALL(op, rev, n)  \
+  return fp_call_internal (op, rev, n, operands, insn);
+
+static char *
+fp_call_internal (op, rev, n, operands, insn)
+     char *op;
+     char *rev;
+     int n;
+     rtx *operands;
+     rtx insn;
+{
+  if (!FP_REG_P (operands[0]))
+    {
+      /* Here destination is in memory
+        and source is in the fp stack.  */
+      output_movf (FP_TOP, operands[0]);
+      output_asm_insn_double_reg_op (op, rev, insn);
+      return "fstp%z0 %0";
+    }
+
+  if (FP_REG_P (operands[n]))
+    {
+      rtx temp = operands[1];
+      char *tem1 = op;
+      operands[1] = operands[n];
+      op = rev;
+      operands[n] = temp;
+      rev = tem1;
+    }
+
+  if (REG_P (operands[n]))
+    {
+      rtx xops[2];
+      via_memory (operands[n]);
+      operands[n] = AT_SP (GET_MODE (operands[n]));
+      xops[0] = stack_pointer_rtx;
+      xops[1] = gen_rtx (CONST_INT, VOIDmode,
+                        GET_MODE_SIZE (GET_MODE (operands[n])));
+      output_asm_insn (op, operands + n);
+      output_asm_insn (AS2 (add%L0,%1,%0), xops);
+    }
+  else
+    output_asm_insn (op, operands + n);
+
+  if (FP_REG_P (operands[0]))
+    {
+      /* It turns out not to work to use top_dead_p because
+        the death notes are not accurate enough.
+        But this ought to work, because the only thing that can
+        live across basic blocks is reg 8, and these insns
+        never involve reg 8 directly.  */
+      if (fp_top_dead_p1 (insn))
+       POP_ONE_FP;
+    }
+
+  RET;
+}
+
+/* Output assembler code to perform insn OP
+   with two stack operands, and output on the stack.
+
+   REV is the assembler insn that does the same thing but
+   effectively interchanges the meanings of the two arguments.
+
+   Somewhat counterintuitively, the "first" operand was pushed last.
+
+   The output replaces either the top-of-stack or both of the arguments,
+   depending on whether the other argument is wanted after this insn.  */
+
+static void
+output_asm_insn_double_reg_op (op, rev, insn)
+     char *op;
+     char *rev;
+     rtx insn;
+{
+  fputc ('\t', asm_out_file);
+  if (top_dead_p (insn))
+    {
+      /* Here we want the "reversed" insn, fsubr or fdivr.
+        But there is an assembler bug in all 80386 assemblers
+        which exchanges the meanings of fsubr and fsub, and of fdivr and fdiv!
+        So use the "unreversed" opcode (which will assemble into
+        the "reversed" insn).  */
+      rev = op;
+
+      while (*rev && *rev != '%')
+       fputc (*rev++, asm_out_file);
+      /* fp_pop_level--; */
+
+      fprintf (asm_out_file, AS2 (p,%sst,%sst(1)), RP, RP);
+    }
+  else
+    {
+      while (*op && *op != '%')
+       fputc (*op++, asm_out_file);
+      fprintf (asm_out_file,AS2 ( ,%sst(1),%sst), RP, RP);
+    }
+  putc ('\n', asm_out_file);
+}
+
+/* Moves X to memory location 8 below stack pointer
+   and returns an RTX for that memory location.
+   X should be a register, in DFmode or SFmode.  */
+
+static rtx
+via_memory (x)
+     rtx x;
+{
+  if (!REG_P (x))
+    abort ();
+  if (GET_MODE (x) == DFmode)
+    {
+      rtx xops[1];
+      xops[0] = gen_rtx (REG, SImode, REGNO (x) + 1);
+      output_asm_insn ("push%L0 %0", xops);
+    }
+  output_asm_insn ("push%L0 %0", &x);
+}
+\f
+/* Output an insn to copy the SFmode value in fp0 to OPERAND
+   without clobbering fp0.  */
+
+void
+fp_store_sf (target)
+     rtx target;
+{
+  if (REG_P (target))
+    {
+      rtx xoperands[3];
+      xoperands[0] = stack_pointer_rtx;
+      xoperands[1] = AT_SP (Pmode);
+      xoperands[2] = gen_rtx (CONST_INT, VOIDmode, -4);
+      output_asm_insn (AS2 (add%L0,%2,%0), xoperands);
+      output_asm_insn ("fst%S0 %1", xoperands);
+      output_asm_insn ("pop%L0 %0", &target);
+    }
+  else if (GET_CODE (target) == MEM)
+    output_asm_insn ("fst%S0 %0", &target);
+}
+
+/* Output an insn to pop an SF value from fp0 into TARGET.
+   This destroys the value of fp0.  */
+
+void
+fp_pop_sf (target)
+     rtx target;
+{
+  if (REG_P (target))
+    {
+      rtx xoperands[3];
+      xoperands[0] = stack_pointer_rtx;
+      xoperands[1] = AT_SP (Pmode);
+      xoperands[2] = gen_rtx (CONST_INT, VOIDmode, -4);
+      output_asm_insn (AS2 (add%L0,%2,%0), xoperands);
+      output_asm_insn ("fstp%S0 %1", xoperands);
+      output_asm_insn ("pop%L0 %0", &target);
+      /* fp_pop_level--; */
+    }
+  else if (GET_CODE (target) == MEM)
+    {
+      /* fp_pop_level--; */
+      output_asm_insn ("fstp%S0 %0", &target);
+    }
+  else abort ();
+}
+\f
+/* Copy the top of the fpu stack into TARGET, without popping.  */
+
+void
+fp_store_df (target)
+     rtx target;
+{
+  if (REG_P (target))
+    {
+      rtx xoperands[4];
+      xoperands[0] = stack_pointer_rtx;
+      xoperands[1] = gen_rtx (REG, SImode, REGNO (target) + 1);
+      xoperands[2] = AT_SP (Pmode);
+      xoperands[3] = gen_rtx (CONST_INT, VOIDmode, -8);
+      output_asm_insn (AS2 (add%L0,%3,%0), xoperands);
+      output_asm_insn ("fst%Q0 %2", xoperands);
+      output_asm_insn ("pop%L0 %0", &target);
+      output_asm_insn ("pop%L0 %1", xoperands);
+    }
+  else if (GET_CODE (target) == MEM)
+    output_asm_insn ("fst%Q0 %0", &target);
+}
+
+/* Copy the top of the fpu stack into TARGET, with popping.  */
+
+void
+fp_pop_df (target)
+     rtx target;
+{
+  if (REG_P (target))
+    {
+      rtx xoperands[4];
+      xoperands[0] = stack_pointer_rtx;
+      xoperands[1] = gen_rtx (REG, SImode, REGNO (target) + 1);
+      xoperands[2] = AT_SP (Pmode);
+      xoperands[3] = gen_rtx (CONST_INT, VOIDmode, -8);
+      output_asm_insn (AS2 (add%L0,%3,%0), xoperands);
+      /* fp_pop_level--; */
+      output_asm_insn ("fstp%Q0 %2", xoperands);
+      output_asm_insn ("pop%L0 %0", &target);
+      output_asm_insn ("pop%L0 %1", xoperands);
+    }
+  else if (GET_CODE (target) == MEM)
+    {
+      /* fp_pop_level--; */
+      output_asm_insn ("fstp%z0 %0", &target);
+    }
+}
+\f
+#if 0
+/* Pop the fp stack, convert value to integer and store in TARGET.
+   TARGET may be memory or register, and may have QI, HI or SImode.  */
+
+void
+fp_pop_int (target)
+     rtx target;
+{
+  if (REG_P (target) || GET_MODE (target) != SImode)
+    {
+      rtx xxops[2];
+      xxops[0] = stack_pointer_rtx;
+      xxops[1] = gen_rtx (CONST_INT, VOIDmode, 4);
+      output_asm_insn (AS2 (sub%L0,%1,%0), xxops);
+      xxops[0] = AT_SP (Pmode);
+      /* fp_pop_level--; */
+      output_asm_insn ("fistp%L0 %0", xxops);
+      output_asm_insn ("pop%L0 %0", &target);
+    }
+  else if (GET_CODE (target) == MEM)
+    {
+      /* fp_pop_level--; */
+      output_asm_insn ("fistp%L0 %0", &target);
+    }
+  else abort ();
+}
+#endif
+\f
+/* Push the SFmode value X onto the fpu stack.  */
+
+void
+fp_push_sf (x)
+     rtx x;
+{
+  /* fp_pop_level++; */
+  if (REG_P (x))
+    {
+      rtx xoperands[2];
+      rtx xfops[3];
+      output_asm_insn ("push%L0 %0", &x);
+      xfops[0] = AT_SP (Pmode);
+      xfops[2] = gen_rtx (CONST_INT, VOIDmode, 4);
+      xfops[1] = stack_pointer_rtx;
+      output_asm_insn ("fld%S0 %0\n\tadd%L0 %2,%1", xfops);
+    }
+  else
+    output_asm_insn ("fld%S0 %0", &x);
+}
+
+/* Push the DFmode value X onto the fpu stack.  */
+
+void
+fp_push_df (x)
+     rtx x;
+{
+  /* fp_pop_level++; */
+
+  if (REG_P (x))
+    {
+      rtx xoperands[2];
+      rtx xfops[3];
+      xoperands[0] = x;
+      xoperands[1] = gen_rtx (REG, SImode, REGNO (x) + 1);
+      output_asm_insn ("push%L0 %1", xoperands);
+      output_asm_insn ("push%L0 %0", xoperands);
+      xfops[0] = AT_SP (Pmode);
+      xfops[2] = gen_rtx (CONST_INT, VOIDmode, 8);
+      xfops[1] = stack_pointer_rtx;
+      output_asm_insn ("fld%Q0 %0\n\tadd%L0 %2,%1", xfops);
+    }
+  else if (GET_CODE (x) == MEM)
+    output_asm_insn ("fld%Q0 %0", &x);
+}
+\f
+static char *output_move_const_single ();
+
+static char *
+singlemove_string (operands)
+     rtx *operands;
+{
+  rtx x;
+  if (GET_CODE (operands[0]) == MEM
+      && GET_CODE (x = XEXP (operands[0], 0)) == PRE_DEC)
+    {
+      if (XEXP (x, 0) != stack_pointer_rtx)
+       abort ();
+      return "push%L0 %1";
+    }
+  else if (GET_CODE (operands[1]) == CONST_DOUBLE)
+    {
+      return output_move_const_single (operands);
+    }
+  else if (GET_CODE (operands[0]) == REG || GET_CODE (operands[1]) == REG)
+    return AS2 (mov%L0,%1,%0);
+  else if (CONSTANT_P (operands[1]))
+    return AS2 (mov%L0,%1,%0);
+  else
+    {
+      output_asm_insn ("push%L0 %1", operands);
+      return "pop%L0 %0";
+    }
+}
+\f
+/* Return a REG that occurs in ADDR with coefficient 1.
+   ADDR can be effectively incremented by incrementing REG.  */
+
+static rtx
+find_addr_reg (addr)
+     rtx addr;
+{
+  while (GET_CODE (addr) == PLUS)
+    {
+      if (GET_CODE (XEXP (addr, 0)) == REG)
+       addr = XEXP (addr, 0);
+      else if (GET_CODE (XEXP (addr, 1)) == REG)
+       addr = XEXP (addr, 1);
+      else if (CONSTANT_P (XEXP (addr, 0)))
+       addr = XEXP (addr, 1);
+      else if (CONSTANT_P (XEXP (addr, 1)))
+       addr = XEXP (addr, 0);
+      else
+       abort ();
+    }
+  if (GET_CODE (addr) == REG)
+    return addr;
+  abort ();
+}
+
+/* Output an insn to add the constant N to the register X.  */
+
+static void
+asm_add (n, x)
+     int n;
+     rtx x;
+{
+  rtx xops[2];
+  xops[1] = x;
+  if (n < 0)
+    {
+      xops[0] = gen_rtx (CONST_INT, VOIDmode, -n);
+      output_asm_insn (AS2 (sub%L0,%0,%1), xops);
+    }
+  else if (n > 0)
+    {
+      xops[0] = gen_rtx (CONST_INT, VOIDmode, n);
+      output_asm_insn (AS2 (add%L0,%0,%1), xops);
+    }
+}
+
+/* Output assembler code to perform a doubleword move insn
+   with operands OPERANDS.  */
+
+char *
+output_move_double (operands)
+     rtx *operands;
+{
+  enum {REGOP, OFFSOP, MEMOP, PUSHOP, POPOP, CNSTOP, RNDOP } optype0, optype1;
+  rtx latehalf[2];
+  rtx addreg0 = 0, addreg1 = 0;
+
+  /* First classify both operands.  */
+
+  if (REG_P (operands[0]))
+    optype0 = REGOP;
+  else if (offsettable_memref_p (operands[0]))
+    optype0 = OFFSOP;
+  else if (GET_CODE (XEXP (operands[0], 0)) == POST_INC)
+    optype0 = POPOP;
+  else if (GET_CODE (XEXP (operands[0], 0)) == PRE_DEC)
+    optype0 = PUSHOP;
+  else if (GET_CODE (operands[0]) == MEM)
+    optype0 = MEMOP;
+  else
+    optype0 = RNDOP;
+
+  if (REG_P (operands[1]))
+    optype1 = REGOP;
+  else if (CONSTANT_P (operands[1])
+          || GET_CODE (operands[1]) == CONST_DOUBLE)
+    optype1 = CNSTOP;
+  else if (offsettable_memref_p (operands[1]))
+    optype1 = OFFSOP;
+  else if (GET_CODE (XEXP (operands[1], 0)) == POST_INC)
+    optype1 = POPOP;
+  else if (GET_CODE (XEXP (operands[1], 0)) == PRE_DEC)
+    optype1 = PUSHOP;
+  else if (GET_CODE (operands[1]) == MEM)
+    optype1 = MEMOP;
+  else
+    optype1 = RNDOP;
+
+  /* Check for the cases that the operand constraints are not
+     supposed to allow to happen.  Abort if we get one,
+     because generating code for these cases is painful.  */
+
+  if (optype0 == RNDOP || optype1 == RNDOP)
+    abort ();
+
+  /* If one operand is decrementing and one is incrementing
+     decrement the former register explicitly
+     and change that operand into ordinary indexing.  */
+
+  if (optype0 == PUSHOP && optype1 == POPOP)
+    {
+      operands[0] = XEXP (XEXP (operands[0], 0), 0);
+      asm_add (-8, operands[0]);
+      operands[0] = gen_rtx (MEM, DImode, operands[0]);
+      optype0 = OFFSOP;
+    }
+  if (optype0 == POPOP && optype1 == PUSHOP)
+    {
+      operands[1] = XEXP (XEXP (operands[1], 0), 0);
+      asm_add (-8, operands[1]);
+      operands[1] = gen_rtx (MEM, DImode, operands[1]);
+      optype1 = OFFSOP;
+    }
+
+  /* If an operand is an unoffsettable memory ref, find a register
+     we can increment temporarily to make it refer to the second word.  */
+
+  if (optype0 == MEMOP)
+    addreg0 = find_addr_reg (XEXP (operands[0], 0));
+
+  if (optype1 == MEMOP)
+    addreg1 = find_addr_reg (XEXP (operands[1], 0));
+
+  /* Ok, we can do one word at a time.
+     Normally we do the low-numbered word first,
+     but if either operand is autodecrementing then we
+     do the high-numbered word first.
+
+     In either case, set up in LATEHALF the operands to use
+     for the high-numbered word and in some cases alter the
+     operands in OPERANDS to be suitable for the low-numbered word.  */
+
+  if (optype0 == REGOP)
+    latehalf[0] = gen_rtx (REG, SImode, REGNO (operands[0]) + 1);
+  else if (optype0 == OFFSOP)
+    latehalf[0] = adj_offsettable_operand (operands[0], 4);
+  else
+    latehalf[0] = operands[0];
+
+  if (optype1 == REGOP)
+    latehalf[1] = gen_rtx (REG, SImode, REGNO (operands[1]) + 1);
+  else if (optype1 == OFFSOP)
+    latehalf[1] = adj_offsettable_operand (operands[1], 4);
+  else if (optype1 == CNSTOP)
+    {
+      if (CONSTANT_P (operands[1]))
+       latehalf[1] = const0_rtx;
+      else if (GET_CODE (operands[1]) == CONST_DOUBLE)
+       {
+         latehalf[1] = gen_rtx (CONST_INT, VOIDmode,
+                                CONST_DOUBLE_HIGH (operands[1]));
+         operands[1] = gen_rtx (CONST_INT, VOIDmode,
+                                CONST_DOUBLE_LOW (operands[1]));
+       }
+    }
+  else
+    latehalf[1] = operands[1];
+
+  /* If insn is effectively movd N (sp),-(sp) then we will do the
+     high word first.  We should use the adjusted operand 1 (which is N+4 (sp))
+     for the low word as well, to compensate for the first decrement of sp.  */
+  if (optype0 == PUSHOP
+      && REGNO (XEXP (XEXP (operands[0], 0), 0)) == STACK_POINTER_REGNUM
+      && reg_overlap_mentioned_p (stack_pointer_rtx, operands[1]))
+    operands[1] = latehalf[1];
+
+  /* If one or both operands autodecrementing,
+     do the two words, high-numbered first.  */
+
+  /* Likewise,  the first move would clobber the source of the second one,
+     do them in the other order.  This happens only for registers;
+     such overlap can't happen in memory unless the user explicitly
+     sets it up, and that is an undefined circumstance.  */
+
+  if (optype0 == PUSHOP || optype1 == PUSHOP
+      || (optype0 == REGOP && optype1 == REGOP
+         && REGNO (operands[0]) == REGNO (latehalf[1])))
+    {
+      /* Make any unoffsettable addresses point at high-numbered word.  */
+      if (addreg0)
+       asm_add (4, addreg0);
+      if (addreg1)
+       asm_add (4, addreg1);
+
+      /* Do that word.  */
+      output_asm_insn (singlemove_string (latehalf), latehalf);
+
+      /* Undo the adds we just did.  */
+      if (addreg0)
+         asm_add (-4, addreg0);
+      if (addreg1)
+       asm_add (-4, addreg1);
+
+      /* Do low-numbered word.  */
+      return singlemove_string (operands);
+    }
+
+  /* Normal case: do the two words, low-numbered first.  */
+
+  output_asm_insn (singlemove_string (operands), operands);
+
+  /* Make any unoffsettable addresses point at high-numbered word.  */
+  if (addreg0)
+    asm_add (4, addreg0);
+  if (addreg1)
+    asm_add (4, addreg1);
+
+  /* Do that word.  */
+  output_asm_insn (singlemove_string (latehalf), latehalf);
+
+  /* Undo the adds we just did.  */
+  if (addreg0)
+    asm_add (-4, addreg0);
+  if (addreg1)
+    asm_add (-4, addreg1);
+
+  return "";
+}
+\f
+int
+standard_80387_constant_p (x)
+     rtx x;
+{
+  union { double d; int i[2];} u;
+  register double d;
+  u.i[0] = XINT (x, 0);
+  u.i[1] = XINT (x, 1);
+  d = u.d;
+
+  if (d == 0)
+    return 1;
+  if (d == 1)
+    return 2;
+  /* Note that on the 80387, other constants, such as pi,
+     are much slower to load as standard constants
+     than to load from doubles in memory!  */
+
+  return 0;
+}
+
+static char *
+output_move_const_double (operands)
+     rtx *operands;
+{
+  if (FP_REG_P (operands[0]))
+    {
+      int conval = standard_80387_constant_p (operands[1]);
+
+      /* fp_pop_level++; */
+      if (conval == 1)
+       return "fldz";
+      if (conval == 2)
+       return "fld1";
+      /* fp_pop_level--; */
+    }
+
+  output_move_double (operands);
+}
+
+
+static char *
+output_move_const_single (operands)
+     rtx *operands;
+{
+  if (FP_REG_P (operands[0]))
+    {
+      int conval = standard_80387_constant_p (operands[1]);
+
+      /* fp_pop_level++; */
+      if (conval == 1)
+       return "fldz";
+      if (conval == 2)
+       return "fld1";
+      /* fp_pop_level--; */
+    }
+  if (GET_CODE (operands[1]) == CONST_DOUBLE)
+    {
+      union { int i[2]; double d;} u1;
+      union { int i; float f;} u2;
+      u1.i[0] = CONST_DOUBLE_LOW (operands[1]);
+      u1.i[1] = CONST_DOUBLE_HIGH (operands[1]);
+      u2.f = u1.d;
+      operands[1] = gen_rtx (CONST_INT, VOIDmode, u2.i);
+    }
+  return singlemove_string (operands);
+}
+\f
+/* Output an insn to move an SF value from FROM to TO.
+   The kinds of operands are not restricted
+   except that they may not both be in memory.  */
+
+void
+output_movsf (to, from)
+     rtx from, to;
+{
+  rtx xops[2];
+  xops[0] = to;
+  xops[1] = from;
+  if (FP_REG_P (from) || FP_REG_P (to))
+    {
+      from = xops[1];
+    }
+
+  if (FP_REG_P (from))
+    {
+#if 0
+       {
+         if (REGNO (from) != REGNO (to))
+           {
+             output_asm_insn ("fld%S0 %1\n\tfstp%S0 %0", xops);
+           }
+       }
+      else
+#endif
+
+      if (! FP_REG_P (to))
+       fp_pop_sf (to);
+    }
+  else if (FP_REG_P (to))
+    fp_push_sf (from);
+  else
+    output_asm_insn (singlemove_string (xops), xops);
+}
+
+/* Output an insn to move a DF value from FROM to TO.
+   The kinds of operands are not restricted
+   except that they may not both be in memory.  */
+
+void
+output_movdf (to, from)
+     rtx from, to;
+{
+  rtx xops[2];
+  xops[0] = to;
+  xops[1] = from;
+  if (FP_REG_P (from) || FP_REG_P (to))
+    {
+      from = xops[1];
+      to = xops[0];
+    }
+  if (FP_REG_P (from))
+    {
+#if 0
+       {
+         if (REGNO (from) != REGNO (to))
+           abort ();
+/*         output_asm_insn ("fld%Q0 %1\n\t fstp%Q0 %0", xops);*/
+       }
+      else
+       {
+#endif
+      if (! FP_REG_P (to))
+       fp_pop_df (to);
+    }
+  else if (FP_REG_P (to))
+    fp_push_df (from);
+  else
+    output_asm_insn (output_move_double (xops), xops);
+}
+
+/* does move of FROM to TO where the mode is the minimum of the
+two */
+
+static void
+output_movf (to, from)
+     rtx to, from;
+{
+  if (GET_MODE (from) == SFmode || GET_MODE (to) == SFmode)
+    output_movsf (to, from);
+  else
+    output_movdf (to, from);
+}
+\f
+/* Return the best assembler insn template
+   for moving operands[1] into operands[0] as a fullword.  */
+
+void
+function_prologue (file, size)
+     FILE *file;
+     int size;
+{
+  register int regno;
+  int nregs, limit;
+  rtx xops[4];
+  extern int frame_pointer_needed;
+
+  /* fp_pop_level = 0; */
+  xops[0] = stack_pointer_rtx;
+  xops[1] = frame_pointer_rtx;
+  xops[2] = gen_rtx (CONST_INT, VOIDmode, size);
+  if (frame_pointer_needed)
+    {
+      output_asm_insn ("push%L0 %1", xops);
+      output_asm_insn (AS2 (mov%L0,%0,%1), xops);
+      if (size)
+       output_asm_insn (AS2 (sub%L0,%2,%0), xops);
+    }
+
+  /* Note If use enter it is NOT reversed args.
+     This one is not reversed from intel!!
+     I think enter is slower.  Also sdb doesn't like it.
+     But if you want it the code is:
+     {
+     xops[3] = const0_rtx;
+     output_asm_insn ("enter %2,%3", xops);
+     }
+     */
+  nregs = 0;
+  limit = (frame_pointer_needed ? FRAME_POINTER_REGNUM : STACK_POINTER_REGNUM);
+  for (regno = limit - 1; regno >= 0; regno--)
+    if (regs_ever_live[regno] && ! call_used_regs[regno])
+      {
+       fprintf (file, "\tpush%s %se%s\n", L_SIZE, RP, hi_reg_name[regno]);
+      }
+}
+
+void
+function_epilogue (file, size)
+     FILE *file;
+     int size;
+{
+  register int regno;
+  register int nregs, limit;
+  int assure_sp_pos;
+  int return_struct_adjust;
+  extern int frame_pointer_needed;
+  extern int current_function_pops_args;
+  extern int current_function_args_size;
+  extern int flag_pcc_struct_return;
+
+  limit = (frame_pointer_needed ? FRAME_POINTER_REGNUM : STACK_POINTER_REGNUM);
+  nregs = 0;
+
+  return_struct_adjust =
+    (current_function_returns_struct
+#ifdef STRUCT_RETURN_CALLER_POP
+     && !flag_pcc_struct_return
+#endif
+     ? 4 : 0);
+
+  for (regno = (limit -1); regno >= 0; regno--)
+    if (regs_ever_live[regno] && ! call_used_regs[regno])
+      nregs++;
+
+  /* sp is often  unreliable so we must go off the frame pointer,
+   */
+
+  if (nregs && frame_pointer_needed)
+    {
+      rtx xops[2];
+      xops[0] = adj_offsettable_operand (AT_BP (Pmode),
+                                        -size -(nregs*(UNITS_PER_WORD)));
+      xops[1] = stack_pointer_rtx;
+      output_asm_insn (AS2 (lea%L0,%0,%1), xops);
+    }
+  for (regno = 0; regno < limit; regno++)
+    {
+      if (regs_ever_live[regno] && ! call_used_regs[regno])
+       {
+         fprintf (file, "\tpop%s ", L_SIZE);
+         fprintf (file, "%se%s\n", RP, hi_reg_name[regno]);
+       }
+    }
+
+  if (frame_pointer_needed)
+    fprintf (file, "\tleave\n");
+  if (current_function_pops_args && current_function_args_size)
+    fprintf (file, "\tret %s%d\n", IP,
+            (current_function_args_size + return_struct_adjust));
+  else if (return_struct_adjust)
+    fprintf (file, "\tret %s%d\n", IP, return_struct_adjust);
+  else
+    fprintf (file, "\tret\n");
+}
+
+int
+hard_regno_mode_ok (regno, mode)
+     int regno;
+     enum machine_mode mode;
+{
+  return
+    (regno < 2 ? 1
+     /* Used to reject floating modes here */
+     : regno < 4 ? 1
+     : regno >= 8 ? mode == DFmode || mode == SFmode
+     : mode != QImode);
+}
+\f
+/* Print the name of a register based on its machine mode and number.
+   If CODE is 'w', pretend the mode is HImode.
+   If CODE is 'b', pretend the mode is QImode.
+   If CODE is 'k', pretend the mode is SImode.  */
+
+#define PRINT_REG(X, CODE, FILE) \
+  do { fprintf (FILE, "%s", RP);                       \
+       switch ((CODE == 'w' ? 2                        \
+               : CODE == 'b' ? 1                       \
+               : CODE == 'k' ? 4                       \
+               : GET_MODE_SIZE (GET_MODE (X))))        \
+        {                                              \
+        case 4:                                        \
+        case 8:                                        \
+          if (!FP_REG_P (X)) fputs ("e", FILE);        \
+        case 2:                                        \
+          fputs (hi_reg_name[REGNO (X)], FILE);        \
+          break;                                       \
+        case 1:                                        \
+          fputs (qi_reg_name[REGNO (X)], FILE);        \
+          break;                                       \
+        }                                              \
+     } while (0)
+
+/* Meaning of CODE:
+   f -- float insn (print a CONST_DOUBLE as a float rather than in hex).
+   L,W,B,Q,S -- print the opcode suffix for specified size of operand.
+   R -- print the prefix for register names.
+   z -- print the opcode suffix for the size of the current operand.
+   * -- print a star (in certain assembler syntax)
+   w -- print the operand as if it's a "word" (HImode) even if it isn't.
+   c -- don't print special prefixes before constant operands.
+*/
+
+void
+print_operand (file, x, code)
+     FILE *file;
+     rtx x;
+     int code;
+{
+  if (code)
+    {
+      switch (code)
+       {
+       case '*':
+         if (USE_STAR)
+           putc ('*', file);
+         return;
+
+       case 'L':
+         PUT_OP_SIZE (code, 'l', file);
+         return;
+
+       case 'W':
+         PUT_OP_SIZE (code, 'w', file);
+         return;
+
+       case 'B':
+         PUT_OP_SIZE (code, 'b', file);
+         return;
+
+       case 'Q':
+         PUT_OP_SIZE (code, 'l', file);
+         return;
+
+       case 'S':
+         PUT_OP_SIZE (code, 's', file);
+         return;
+
+       case 'R':
+         fprintf (file, "%s", RP);
+         return;
+
+       case 'z':
+         /* this is the size of op from size of operand */
+         switch (GET_MODE_SIZE (GET_MODE (x)))
+           {
+           case 2:
+             PUT_OP_SIZE ('W', 'w', file);
+             return;
+           case 4:
+             if (GET_MODE (x) == SFmode)
+               {
+                 PUT_OP_SIZE ('S', 's', file);
+                 return;
+               }
+             else
+               PUT_OP_SIZE ('L', 'l', file);
+             return;
+           case 8:
+             if (!FP_REG_P (x)) PUT_OP_SIZE ('Q', 'l', file);
+             return;
+           case 1:
+             PUT_OP_SIZE ('B', 'b', file);
+             return;
+           }
+       }
+    }
+  if (GET_CODE (x) == REG)
+    {
+      PRINT_REG (x, code, file);
+    }
+  else if (GET_CODE (x) == MEM)
+    {
+      PRINT_PTR (x, file);
+      if (CONSTANT_ADDRESS_P (XEXP (x, 0)))
+       output_addr_const (file, XEXP (x, 0));
+      else
+       output_address (XEXP (x, 0));
+    }
+  else if (GET_CODE (x) == CONST_DOUBLE && GET_MODE (x) == SFmode)
+    {
+      union { double d; int i[2]; } u;
+      union { float f; int i; } u1;
+      u.i[0] = CONST_DOUBLE_LOW (x);
+      u.i[1] = CONST_DOUBLE_HIGH (x);
+      u1.f = u.d;
+      if (code == 'f')
+        fprintf (file, "%.22e", u1.f);
+      else
+        {
+         PRINT_IMMED_PREFIX (file);
+         fprintf (file, "0x%x", u1.i);
+       }
+    }
+  else if (GET_CODE (x) == CONST_DOUBLE && GET_MODE (x) == DFmode)
+    {
+      union { double d; int i[2]; } u;
+      u.i[0] = CONST_DOUBLE_LOW (x);
+      u.i[1] = CONST_DOUBLE_HIGH (x);
+      fprintf (file, "%.22e", u.d);
+    }
+  else 
+    {
+      if (code != 'c')
+       {
+         if (GET_CODE (x) == CONST_INT)
+           PRINT_IMMED_PREFIX (file);
+         else if (GET_CODE (x) == CONST || GET_CODE (x) == SYMBOL_REF)
+           PRINT_OFFSET_PREFIX (file);
+       }
+      output_addr_const (file, x);
+    }
+}
+\f
+/* Print a memory operand whose address is ADDR.  */
+
+void
+print_operand_address (file, addr)
+     FILE *file;
+     register rtx addr;
+{
+  register rtx reg1, reg2, breg, ireg;
+  rtx offset;
+
+  switch (GET_CODE (addr))
+    {
+    case REG:
+      ADDR_BEG (file);
+      fprintf (file, "%se", RP);
+      fputs (hi_reg_name[REGNO (addr)], file);
+      ADDR_END (file);
+      break;
+
+    case PLUS:
+      reg1 = 0;
+      reg2 = 0;
+      ireg = 0;
+      breg = 0;
+      offset = 0;
+      if (CONSTANT_ADDRESS_P (XEXP (addr, 0)))
+       {
+         offset = XEXP (addr, 0);
+         addr = XEXP (addr, 1);
+       }
+      else if (CONSTANT_ADDRESS_P (XEXP (addr, 1)))
+       {
+         offset = XEXP (addr, 1);
+         addr = XEXP (addr, 0);
+       }
+      if (GET_CODE (addr) != PLUS) ;
+      else if (GET_CODE (XEXP (addr, 0)) == MULT)
+       {
+         reg1 = XEXP (addr, 0);
+         addr = XEXP (addr, 1);
+       }
+      else if (GET_CODE (XEXP (addr, 1)) == MULT)
+       {
+         reg1 = XEXP (addr, 1);
+         addr = XEXP (addr, 0);
+       }
+      else if (GET_CODE (XEXP (addr, 0)) == REG)
+       {
+         reg1 = XEXP (addr, 0);
+         addr = XEXP (addr, 1);
+       }
+      else if (GET_CODE (XEXP (addr, 1)) == REG)
+       {
+         reg1 = XEXP (addr, 1);
+         addr = XEXP (addr, 0);
+       }
+      if (GET_CODE (addr) == REG || GET_CODE (addr) == MULT)
+       {
+         if (reg1 == 0) reg1 = addr;
+         else reg2 = addr;
+         addr = 0;
+       }
+      if (offset != 0)
+       {
+         if (addr != 0) abort ();
+         addr = offset;
+       }
+      if ((reg1 && GET_CODE (reg1) == MULT)
+         || (reg2 != 0 && REGNO_OK_FOR_BASE_P (REGNO (reg2))))
+       {
+         breg = reg2;
+         ireg = reg1;
+       }
+      else if (reg1 != 0 && REGNO_OK_FOR_BASE_P (REGNO (reg1)))
+       {
+         breg = reg1;
+         ireg = reg2;
+       }
+
+      if (ireg != 0 || breg != 0)
+       {
+         int scale = 1;
+
+         if (addr != 0)
+           {
+             if (GET_CODE (addr) == LABEL_REF)
+               output_asm_label (addr);
+             else
+               output_addr_const (file, addr);
+           }
+
+         if (ireg != 0 && GET_CODE (ireg) == MULT)
+           {
+             scale = INTVAL (XEXP (ireg, 1));
+             ireg = XEXP (ireg, 0);
+           }
+         /* output breg+ireg*scale */
+         PRINT_B_I_S (breg, ireg, scale, file);
+         break;
+       }
+
+    case MULT:
+      {
+       int scale;
+       if (GET_CODE (XEXP (addr, 0)) == CONST_INT)
+         {
+           scale = INTVAL (XEXP (addr, 0));
+           ireg = XEXP (addr, 1);
+         }
+       else
+         {
+           scale = INTVAL (XEXP (addr, 1));
+           ireg = XEXP (addr, 0);
+         }
+       output_addr_const (file, const0_rtx);
+       PRINT_B_I_S ((rtx) 0, ireg, scale, file);
+      }
+      break;
+
+    default:
+      if (GET_CODE (addr) == CONST_INT
+         && INTVAL (addr) < 0x8000
+         && INTVAL (addr) >= -0x8000)
+       fprintf (file, "%d", INTVAL (addr));
+      else
+       output_addr_const (file, addr);
+    }
+}
+\f
+/* Set the cc_status for the results of an insn whose pattern is EXP.
+   On the 80386, we assume that only test and compare insns, as well
+   as SI, HI, & DI mode ADD, SUB, NEG, AND, IOR, XOR, ASHIFT, LSHIFT,
+   ASHIFTRT, and LSHIFTRT instructions set the condition codes usefully.
+   Also, we assume that jumps and moves don't affect the condition codes.
+   All else, clobbers the condition codes, by assumption.
+
+   We assume that ALL add, minus, etc. instructions effect the condition
+   codes.  This MUST be consistent with i386.md.  */
+
+notice_update_cc (exp)
+     rtx exp;
+{
+  if (GET_CODE (exp) == SET)
+    {
+      /* Jumps do not alter the cc's.  */
+      if (SET_DEST (exp) == pc_rtx)
+       return;
+      /* Moving register or memory into a register:
+        it doesn't alter the cc's, but it might invalidate
+        the RTX's which we remember the cc's came from.
+        (Note that moving a constant 0 or 1 MAY set the cc's).  */
+      if (REG_P (SET_DEST (exp))
+         && (REG_P (SET_SRC (exp)) || GET_CODE (SET_SRC (exp)) == MEM))
+       {
+         if (cc_status.value1
+             && reg_overlap_mentioned_p (SET_DEST (exp), cc_status.value1))
+           cc_status.value1 = 0;
+         if (cc_status.value2
+             && reg_overlap_mentioned_p (SET_DEST (exp), cc_status.value2))
+           cc_status.value2 = 0;
+         return;
+       }
+      /* Moving register into memory doesn't alter the cc's.
+        It may invalidate the RTX's which we remember the cc's came from.  */
+      if (GET_CODE (SET_DEST (exp)) == MEM && REG_P (SET_SRC (exp)))
+       {
+         if (cc_status.value1 && GET_CODE (cc_status.value1) == MEM)
+           cc_status.value1 = 0;
+         if (cc_status.value2 && GET_CODE (cc_status.value2) == MEM)
+           cc_status.value2 = 0;
+         return;
+       }
+      /* Function calls clobber the cc's.  */
+      else if (GET_CODE (SET_SRC (exp)) == CALL)
+       {
+         CC_STATUS_INIT;
+         return;
+       }
+      /* Tests and compares set the cc's in predictable ways.  */
+      else if (SET_DEST (exp) == cc0_rtx)
+       {
+         CC_STATUS_INIT;
+         cc_status.value1 = SET_SRC (exp);
+         return;
+       }
+      /* Certain instructions effect the condition codes. */
+      else if (GET_MODE (SET_SRC (exp)) == SImode
+              || GET_MODE (SET_SRC (exp)) == HImode
+              || GET_MODE (SET_SRC (exp)) == QImode)
+       switch (GET_CODE (SET_SRC (exp)))
+         {
+         case ASHIFTRT: case LSHIFTRT:
+         case ASHIFT: case LSHIFT:
+           /* Shifts on the 386 don't set the condition codes if the
+              shift count is zero. */
+           if (GET_CODE (XEXP (SET_SRC (exp), 1)) != CONST_INT)
+             {
+               CC_STATUS_INIT;
+               break;
+             }
+           /* We assume that the CONST_INT is non-zero (this rtx would
+              have been deleted if it were zero. */
+
+         case PLUS: case MINUS: case NEG:
+         case AND: case IOR: case XOR:
+           cc_status.flags = CC_NO_OVERFLOW;
+           cc_status.value1 = SET_SRC (exp);
+           cc_status.value2 = SET_DEST (exp);
+           break;
+
+         default:
+           CC_STATUS_INIT;
+         }
+      else
+       {
+         CC_STATUS_INIT;
+       }
+    }
+  else if (GET_CODE (exp) == PARALLEL
+          && GET_CODE (XVECEXP (exp, 0, 0)) == SET)
+    {
+      if (SET_DEST (XVECEXP (exp, 0, 0)) == pc_rtx)
+       return;
+      if (SET_DEST (XVECEXP (exp, 0, 0)) == cc0_rtx)
+       {
+         CC_STATUS_INIT;
+         cc_status.value1 = SET_SRC (XVECEXP (exp, 0, 0));
+         return;
+       }
+      CC_STATUS_INIT;
+    }
+  else
+    {
+      CC_STATUS_INIT;
+    }
+}
+\f
+/* Nonzero if the top of the fpu stack dies in this insn.  */
+
+int
+top_dead_p (insn)
+     rtx insn;
+{
+  extern int optimize;
+  if (optimize)
+    return (find_regno_note (insn, REG_DEAD, FIRST_FLOAT_REG)
+           || find_regno_note (insn, REG_DEAD, FIRST_FLOAT_REG + 1));
+
+  if (GET_CODE (insn) == CALL_INSN)
+    return call_top_dead_p (insn);
+
+  return fp_top_dead_p1 (insn);
+}
+
+/* Following is used after a call_value insn
+   if obey_regdecls there will not be the REG_DEAD notes
+   to go by (there won't be any cross jumping to worry about
+   either), and we depend on seeing if the FP_TOP is used
+   in the next two insn's.  Otherwise we depend on the
+   REG_DEAD notes.
+   */
+
+static int
+call_top_dead_p (insn)
+     rtx insn;
+{
+  int i;
+  for (i = 0; i < 3; i++)
+    {
+      insn = NEXT_INSN (insn);
+      if (insn == 0)
+       return 1;
+      if (GET_CODE (insn) == NOTE || GET_CODE (insn) == CODE_LABEL)
+       continue;
+      if (GET_CODE (insn) == BARRIER)
+       abort ();
+      if (GET_CODE (PATTERN (insn)) == SET
+         && SET_DEST (PATTERN (insn)) != stack_pointer_rtx)
+       return (!(mentions_fp_top (SET_SRC (PATTERN (insn)))));
+      if (GET_CODE (PATTERN (insn)) == CALL)
+       return 1;
+      if (GET_CODE (PATTERN (insn)) == USE)
+       return (! FP_REG_P (XEXP (PATTERN (insn), 0)));
+    }
+  return 1;
+}
+
+/* Return 1 if current val of fpu top-of-stack appears unused
+   in rest of this basic block.  */
+
+static int
+fp_top_dead_p1 (insn)
+     rtx insn;
+{
+  extern int optimize;
+
+  int past_label = 0;
+
+  for (insn = NEXT_INSN (insn); insn; insn = NEXT_INSN (insn))
+    {
+      switch (GET_CODE (insn))
+       {
+       case CALL_INSN:
+         /* Function calls clobber this value, so it's dead.  */
+         return 1;
+
+       case JUMP_INSN:
+         if (! optimize)
+           /* Can't use JUMP_LABEL, but there's no cross-jumping either.  */
+           return 1;
+         if (JUMP_LABEL (insn) == 0)
+           return 1;
+         insn = JUMP_LABEL (insn);
+       case CODE_LABEL:
+         /* Go past one label or follow one jump in case of cross-jumping,
+            which could insert such a label or jump into one basic block.  */
+         if (! optimize)
+           return 1;
+         if (past_label)
+           return 1;
+         past_label = 1;
+         break;
+
+       case INSN:
+         if (GET_CODE (PATTERN (insn)) == SET)
+           {
+             if ((mentions_fp_top (SET_SRC (PATTERN (insn)))))
+               return 0;
+             else if (FP_REG_P (SET_DEST (PATTERN (insn))))
+               return 1;
+           }
+         else if (mentions_fp_top (PATTERN (insn)))
+           return 0;
+         break;
+       }
+    }
+  return 1;
+}
+
+/* Return 1 if X involves an FPU register.  */
+
+static int
+mentions_fp_top (x)
+     rtx x;
+{
+  register RTX_CODE code;
+
+  code = GET_CODE (x);
+  switch (code)
+    {
+    case LABEL_REF:
+    case SYMBOL_REF:
+    case CONST_INT:
+    case CONST:
+    case CC0:
+    case PC:
+    case CLOBBER:
+    case MEM:
+      return 0;
+
+    case REG:
+      return FP_REGNO_P (REGNO (x));
+    }
+
+  /* Recursively scan the operands of this expression.  */
+  {
+    register char *fmt = GET_RTX_FORMAT (code);
+    register int i;
+
+    for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
+      {
+       if (fmt[i] == 'e')
+         {
+           if (mentions_fp_top (XEXP (x, i)))
+             return 1;
+         }
+       if (fmt[i] == 'E')
+         {
+           register int j;
+           for (j = 0; j < XVECLEN (x, i); j++)
+             if (mentions_fp_top (XVECEXP (x, i, j)))
+               return 1;
+         }
+      }
+  }
+  return 0;
+}
+
+/* Some asm-dependent functions. */
+
+#ifdef MASM
+#include "masm386.c"
+#endif