aboutsummaryrefslogtreecommitdiff
path: root/gcc-1.40/config/out-i386.c
diff options
context:
space:
mode:
authorAndrew Lee <alee14498@protonmail.com>2021-08-15 00:34:05 -0400
committerAndrew Lee <alee14498@protonmail.com>2021-08-15 00:34:05 -0400
commit60cc83bf91bfc9bb02f6304b5d6c8234ba6d210f (patch)
treefdc0be85a1ca35e34c3ae2c805fe9b718e3c1091 /gcc-1.40/config/out-i386.c
parentdd8dfab51b832a654365ed00c06bf802ff628bfa (diff)
downloadlinux-0.01-distro-60cc83bf91bfc9bb02f6304b5d6c8234ba6d210f.tar.gz
linux-0.01-distro-60cc83bf91bfc9bb02f6304b5d6c8234ba6d210f.tar.bz2
linux-0.01-distro-60cc83bf91bfc9bb02f6304b5d6c8234ba6d210f.zip
Added gccHEADmaster
Diffstat (limited to 'gcc-1.40/config/out-i386.c')
-rw-r--r--gcc-1.40/config/out-i386.c1418
1 files changed, 1418 insertions, 0 deletions
diff --git a/gcc-1.40/config/out-i386.c b/gcc-1.40/config/out-i386.c
new file mode 100644
index 0000000..958d09f
--- /dev/null
+++ b/gcc-1.40/config/out-i386.c
@@ -0,0 +1,1418 @@
+/* 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;
+
+/* 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);
+}
+
+/* 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 ();
+}
+
+/* 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);
+ }
+}
+
+#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
+
+/* 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);
+}
+
+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";
+ }
+}
+
+/* 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 "";
+}
+
+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);
+}
+
+/* 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);
+}
+
+/* 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);
+}
+
+/* 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
+ || GET_CODE (x) == LABEL_REF)
+ PRINT_OFFSET_PREFIX (file);
+ }
+ output_addr_const (file, x);
+ }
+}
+
+/* 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);
+ }
+}
+
+/* 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;
+ }
+}
+
+/* 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 and also through a jump insn.
+ This is called from top_dead_p and from fp_call_internal. */
+
+static int
+fp_top_dead_p1 (insn)
+ rtx insn;
+{
+ extern int optimize;
+
+ int past_jump = 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:
+ /* Follow one jump in case of cross-jumping,
+ which could insert such a jump into one basic block. */
+ if (! optimize)
+ /* Can't use JUMP_LABEL, but there's no cross-jumping either. */
+ return 1;
+ if (JUMP_LABEL (insn) == 0)
+ return 1;
+ /* Don't scan past a jump and another jump. */
+ if (past_jump)
+ return 1;
+ past_jump = 1;
+ insn = JUMP_LABEL (insn);
+ case CODE_LABEL:
+ if (! optimize)
+ return 1;
+ break;
+
+ case INSN:
+ if (GET_CODE (PATTERN (insn)) == SET)
+ {
+ if ((mentions_fp_top (SET_SRC (PATTERN (insn)))))
+ return 0;
+ else if (mentions_fp_top (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