home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Fresh Fish 7
/
FreshFishVol7.bin
/
bbs
/
gnu
/
gcc-2.3.3-src.lha
/
GNU
/
src
/
amiga
/
gcc-2.3.3
/
config
/
i386.c
< prev
next >
Wrap
C/C++ Source or Header
|
1994-02-06
|
48KB
|
1,835 lines
/* Subroutines for insn-output.c for Intel 80386.
Copyright (C) 1988, 1992 Free Software Foundation, Inc.
This file is part of GNU CC.
GNU CC is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
GNU CC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GNU CC; see the file COPYING. If not, write to
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
#include <stdio.h>
#include "config.h"
#include "rtl.h"
#include "regs.h"
#include "hard-reg-set.h"
#include "real.h"
#include "insn-config.h"
#include "conditions.h"
#include "insn-flags.h"
#include "output.h"
#include "insn-attr.h"
#include "tree.h"
#include "flags.h"
#ifdef EXTRA_CONSTRAINT
/* If EXTRA_CONSTRAINT is defined, then the 'S'
constraint in REG_CLASS_FROM_LETTER will no longer work, and various
asm statements that need 'S' for class SIREG will break. */
error EXTRA_CONSTRAINT conflicts with S constraint letter
/* The previous line used to be #error, but some compilers barf
even if the conditional was untrue. */
#endif
#define AT_BP(mode) (gen_rtx (MEM, (mode), frame_pointer_rtx))
extern FILE *asm_out_file;
extern char *strcat ();
char *singlemove_string ();
char *output_move_const_single ();
char *output_fp_cc0_set ();
char *hi_reg_name[] = HI_REGISTER_NAMES;
char *qi_reg_name[] = QI_REGISTER_NAMES;
char *qi_high_reg_name[] = QI_HIGH_REGISTER_NAMES;
/* Array of the smallest class containing reg number REGNO, indexed by
REGNO. Used by REGNO_REG_CLASS in i386.h. */
enum reg_class regclass_map[FIRST_PSEUDO_REGISTER] =
{
/* ax, dx, cx, bx */
AREG, DREG, CREG, BREG,
/* si, di, bp, sp */
SIREG, DIREG, INDEX_REGS, GENERAL_REGS,
/* FP registers */
FP_TOP_REG, FP_SECOND_REG, FLOAT_REGS, FLOAT_REGS,
FLOAT_REGS, FLOAT_REGS, FLOAT_REGS, FLOAT_REGS,
/* arg pointer */
INDEX_REGS
};
/* Test and compare insns in i386.md store the information needed to
generate branch and scc insns here. */
struct rtx_def *i386_compare_op0, *i386_compare_op1;
struct rtx_def *(*i386_compare_gen)(), *(*i386_compare_gen_eq)();
/* Output an insn whose source is a 386 integer register. SRC is the
rtx for the register, and TEMPLATE is the op-code template. SRC may
be either SImode or DImode.
The template will be output with operands[0] as SRC, and operands[1]
as a pointer to the top of the 386 stack. So a call from floatsidf2
would look like this:
output_op_from_reg (operands[1], AS1 (fild%z0,%1));
where %z0 corresponds to the caller's operands[1], and is used to
emit the proper size suffix.
??? Extend this to handle HImode - a 387 can load and store HImode
values directly. */
void
output_op_from_reg (src, template)
rtx src;
char *template;
{
rtx xops[4];
xops[0] = src;
xops[1] = AT_SP (Pmode);
xops[2] = GEN_INT (GET_MODE_SIZE (GET_MODE (src)));
xops[3] = stack_pointer_rtx;
if (GET_MODE_SIZE (GET_MODE (src)) > UNITS_PER_WORD)
{
rtx high = gen_rtx (REG, SImode, REGNO (src) + 1);
output_asm_insn (AS1 (push%L0,%0), &high);
}
output_asm_insn (AS1 (push%L0,%0), &src);
output_asm_insn (template, xops);
output_asm_insn (AS2 (add%L3,%2,%3), xops);
}
/* Output an insn to pop an value from the 387 top-of-stack to 386
register DEST. The 387 register stack is popped if DIES is true. If
the mode of DEST is an integer mode, a `fist' integer store is done,
otherwise a `fst' float store is done. */
void
output_to_reg (dest, dies)
rtx dest;
int dies;
{
rtx xops[4];
xops[0] = AT_SP (Pmode);
xops[1] = stack_pointer_rtx;
xops[2] = GEN_INT (GET_MODE_SIZE (GET_MODE (dest)));
xops[3] = dest;
output_asm_insn (AS2 (sub%L1,%2,%1), xops);
if (GET_MODE_CLASS (GET_MODE (dest)) == MODE_INT)
{
if (dies)
output_asm_insn (AS1 (fistp%z3,%y0), xops);
else
output_asm_insn (AS1 (fist%z3,%y0), xops);
}
else if (GET_MODE_CLASS (GET_MODE (dest)) == MODE_FLOAT)
{
if (dies)
output_asm_insn (AS1 (fstp%z3,%y0), xops);
else
output_asm_insn (AS1 (fst%z3,%y0), xops);
}
else
abort ();
output_asm_insn (AS1 (pop%L0,%0), &dest);
if (GET_MODE_SIZE (GET_MODE (dest)) > UNITS_PER_WORD)
{
dest = gen_rtx (REG, SImode, REGNO (dest) + 1);
output_asm_insn (AS1 (pop%L0,%0), &dest);
}
}
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%L1 %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%L1 %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_INT (-n);
output_asm_insn (AS2 (sub%L0,%0,%1), xops);
}
else if (n > 0)
{
xops[0] = GEN_INT (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;
int dest_overlapped_low = 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]))
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 (