home *** CD-ROM | disk | FTP | other *** search
- /* 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);
-
- 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;
- extern int frame_pointer_needed;
- extern int current_function_pops_args;
- extern int current_function_args_size;
- limit = (frame_pointer_needed ? FRAME_POINTER_REGNUM : STACK_POINTER_REGNUM);
- nregs = 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
- + (current_function_returns_struct ? 4 : 0)));
- else if (current_function_returns_struct)
- fprintf (file, "\tret %s4\n", IP);
- 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. */
-
- #define PRINT_REG(X, CODE, FILE) \
- do { fprintf (FILE, "%s", RP); \
- switch ((CODE == 'w' ? 2 \
- : CODE == 'b' ? 1 \
- : 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); \
- } \
- } 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);
- }
- }
-
- /* 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. */
-
- static int
- fp_top_dead_p1 (insn)
- rtx insn;
- {
- 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. */
- case JUMP_INSN:
- case CODE_LABEL:
- return 1;
-
- 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
-