diff options
| author | Andrew Lee <alee14498@protonmail.com> | 2021-08-15 00:34:05 -0400 |
|---|---|---|
| committer | Andrew Lee <alee14498@protonmail.com> | 2021-08-15 00:34:05 -0400 |
| commit | 60cc83bf91bfc9bb02f6304b5d6c8234ba6d210f (patch) | |
| tree | fdc0be85a1ca35e34c3ae2c805fe9b718e3c1091 /gcc-1.40/config/out-i386.c | |
| parent | dd8dfab51b832a654365ed00c06bf802ff628bfa (diff) | |
| download | linux-0.01-distro-master.tar.gz linux-0.01-distro-master.tar.bz2 linux-0.01-distro-master.zip | |
Diffstat (limited to 'gcc-1.40/config/out-i386.c')
| -rw-r--r-- | gcc-1.40/config/out-i386.c | 1418 |
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 |
