summaryrefslogtreecommitdiff
path: root/gcc
diff options
context:
space:
mode:
authorjrv <jrv@138bc75d-0d04-0410-961f-82ee72b054a4>1992-02-01 06:00:10 +0000
committerjrv <jrv@138bc75d-0d04-0410-961f-82ee72b054a4>1992-02-01 06:00:10 +0000
commit53d548f5ab7bda50df5534b1c8dd3d635fd6c982 (patch)
tree18a8d2ce4828395a0e6d6363b7f7550e770ade3e /gcc
parent38fc2180c763bd5fe753000b6332f82f02242555 (diff)
downloadgcc-53d548f5ab7bda50df5534b1c8dd3d635fd6c982.tar.gz
Initial revision
git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@264 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'gcc')
-rw-r--r--gcc/config/i386/i386.c1874
-rw-r--r--gcc/config/svr4.h667
2 files changed, 2541 insertions, 0 deletions
diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c
new file mode 100644
index 00000000000..5c9820d8c15
--- /dev/null
+++ b/gcc/config/i386/i386.c
@@ -0,0 +1,1874 @@
+/* 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 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"
+
+#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 ();
+
+static char *hi_reg_name[] = HI_REGISTER_NAMES;
+static char *qi_reg_name[] = QI_REGISTER_NAMES;
+static char *qi_high_reg_name[] = QI_HIGH_REGISTER_NAMES;
+
+/* 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_rtx (CONST_INT, VOIDmode, 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_rtx (CONST_INT, VOIDmode, 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_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]))
+ 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 (GET_CODE (operands[1]) == CONST_DOUBLE)
+ split_double (operands[1], &operands[1], &latehalf[1]);
+ else if (CONSTANT_P (operands[1]))
+ latehalf[1] = const0_rtx;
+ }
+ 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 real_extract u;
+ register double d;
+
+ bcopy (&CONST_DOUBLE_LOW (x), &u, sizeof u);
+ 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;
+}
+
+char *
+output_move_const_single (operands)
+ rtx *operands;
+{
+ if (FP_REG_P (operands[0]))
+ {
+ int conval = standard_80387_constant_p (operands[1]);
+
+ if (conval == 1)
+ return "fldz";
+
+ if (conval == 2)
+ return "fld1";
+ }
+ 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);
+}
+
+/* Returns 1 if OP is either a symbol reference or a sum of a symbol
+ reference and a constant. */
+
+int
+symbolic_operand (op, mode)
+ register rtx op;
+ enum machine_mode mode;
+{
+ switch (GET_CODE (op))
+ {
+ case SYMBOL_REF:
+ case LABEL_REF:
+ return 1;
+ case CONST:
+ op = XEXP (op, 0);
+ return ((GET_CODE (XEXP (op, 0)) == SYMBOL_REF
+ || GET_CODE (XEXP (op, 0)) == LABEL_REF)
+ && GET_CODE (XEXP (op, 1)) == CONST_INT);
+ default:
+ return 0;
+ }
+}
+
+/* Returns 1 if OP contains a symbol reference */
+
+int
+symbolic_reference_mentioned_p (op)
+ rtx op;
+{
+ register char *fmt;
+ register int i;
+
+ if (GET_CODE (op) == SYMBOL_REF || GET_CODE (op) == LABEL_REF)
+ return 1;
+
+ fmt = GET_RTX_FORMAT (GET_CODE (op));
+ for (i = GET_RTX_LENGTH (GET_CODE (op)) - 1; i >= 0; i--)
+ {
+ if (fmt[i] == 'E')
+ {
+ register int j;
+
+ for (j = XVECLEN (op, i) - 1; j >= 0; j--)
+ if (symbolic_reference_mentioned_p (XVECEXP (op, i, j)))
+ return 1;
+ }
+ else if (fmt[i] == 'e' && symbolic_reference_mentioned_p (XEXP (op, i)))
+ return 1;
+ }
+
+ return 0;
+}
+
+/* Return a legitimate reference for ORIG (an address) using the
+ register REG. If REG is 0, a new pseudo is generated.
+
+ There are three types of references that must be handled:
+
+ 1. Global data references must load the address from the GOT, via
+ the PIC reg. An insn is emitted to do this load, and the reg is
+ returned.
+
+ 2. Static data references must compute the address as an offset
+ from the GOT, whose base is in the PIC reg. An insn is emitted to
+ compute the address into a reg, and the reg is returned. Static
+ data objects have SYMBOL_REF_FLAG set to differentiate them from
+ global data objects.
+
+ 3. Constant pool addresses must be handled special. They are
+ considered legitimate addresses, but only if not used with regs.
+ When printed, the output routines know to print the reference with the
+ PIC reg, even though the PIC reg doesn't appear in the RTL.
+
+ GO_IF_LEGITIMATE_ADDRESS rejects symbolic references unless the PIC
+ reg also appears in the address (except for constant pool references,
+ noted above).
+
+ "switch" statements also require special handling when generating
+ PIC code. See comments by the `casesi' insn in i386.md for details. */
+
+rtx
+legitimize_pic_address (orig, reg)
+ rtx orig;
+ rtx reg;
+{
+ rtx addr = orig;
+ rtx new = orig;
+
+ if (GET_CODE (addr) == SYMBOL_REF || GET_CODE (addr) == LABEL_REF)
+ {
+ if (GET_CODE (addr) == SYMBOL_REF && CONSTANT_POOL_ADDRESS_P (addr))
+ reg = new = orig;
+ else
+ {
+ if (reg == 0)
+ reg = gen_reg_rtx (Pmode);
+
+ if (GET_CODE (addr) == SYMBOL_REF && SYMBOL_REF_FLAG (addr))
+ new = gen_rtx (PLUS, Pmode, pic_offset_table_rtx, orig);
+ else
+ new = gen_rtx (MEM, Pmode,
+ gen_rtx (PLUS, Pmode,
+ pic_offset_table_rtx, orig));
+
+ emit_move_insn (reg, new);
+ }
+ current_function_uses_pic_offset_table = 1;
+ return reg;
+ }
+ else if (GET_CODE (addr) == CONST || GET_CODE (addr) == PLUS)
+ {
+ rtx base;
+
+ if (GET_CODE (addr) == CONST)
+ {
+ addr = XEXP (addr, 0);
+ if (GET_CODE (addr) != PLUS)
+ abort ();
+ }
+
+ if (XEXP (addr, 0) == pic_offset_table_rtx)
+ return orig;
+
+ if (reg == 0)
+ reg = gen_reg_rtx (Pmode);
+
+ base = legitimize_pic_address (XEXP (addr, 0), reg);
+ addr = legitimize_pic_address (XEXP (addr, 1), base == reg ? 0 : reg);
+
+ if (GET_CODE (addr) == CONST_INT)
+ return plus_constant (base, INTVAL (addr));
+
+ if (GET_CODE (addr) == PLUS && CONSTANT_P (XEXP (addr, 1)))
+ {
+ base = gen_rtx (PLUS, Pmode, base, XEXP (addr, 0));
+ addr = XEXP (addr, 1);
+ }
+ return gen_rtx (PLUS, Pmode, base, addr);
+ }
+ return new;
+}
+
+/* Emit insns to move operands[1] into operands[0]. */
+
+void
+emit_pic_move (operands, mode)
+ rtx *operands;
+ enum machine_mode mode;
+{
+ rtx temp = reload_in_progress ? operands[0] : gen_reg_rtx (Pmode);
+
+ if (GET_CODE (operands[0]) == MEM && SYMBOLIC_CONST (operands[1]))
+ operands[1] = (rtx) force_reg (SImode, operands[1]);
+ else
+ operands[1] = legitimize_pic_address (operands[1], temp);
+}
+
+/* This function generates the assembly code for function entry.
+ FILE is an stdio stream to output the code to.
+ SIZE is an int: how many units of temporary storage to allocate. */
+
+void
+function_prologue (file, size)
+ FILE *file;
+ int size;
+{
+ register int regno;
+ int limit;
+ rtx xops[4];
+
+ 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%L1 %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);
+ }
+ */
+ 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])
+ || (current_function_uses_pic_offset_table
+ && regno == PIC_OFFSET_TABLE_REGNUM))
+ {
+ xops[0] = gen_rtx (REG, SImode, regno);
+ output_asm_insn ("push%L0 %0", xops);
+ }
+
+ if (current_function_uses_pic_offset_table)
+ {
+ xops[0] = pic_offset_table_rtx;
+ xops[1] = (rtx) gen_label_rtx ();
+
+ output_asm_insn (AS1 (call,%P1), xops);
+ ASM_OUTPUT_INTERNAL_LABEL (file, "L", CODE_LABEL_NUMBER (xops[1]));
+ output_asm_insn (AS1 (pop%L0,%0), xops);
+ output_asm_insn ("addl $_GLOBAL_OFFSET_TABLE_+[.-%P1],%0", xops);
+ }
+}
+
+/* Return 1 if it is appropriate to emit `ret' instructions in the
+ body of a function. Do this only if the epilogue is simple, needing a
+ couple of insns. Prior to reloading, we can't tell how many registers
+ must be saved, so return 0 then.
+
+ If NON_SAVING_SETJMP is defined and true, then it is not possible
+ for the epilogue to be simple, so return 0. This is a special case
+ since NON_SAVING_SETJMP will not cause regs_ever_live to change until
+ final, but jump_optimize may need to know sooner if a `return' is OK. */
+
+int
+simple_386_epilogue ()
+{
+ int regno;
+ int nregs = 0;
+ int reglimit = (frame_pointer_needed
+ ? FRAME_POINTER_REGNUM : STACK_POINTER_REGNUM);
+
+#ifdef NON_SAVING_SETJMP
+ if (NON_SAVING_SETJMP && current_function_calls_setjmp)
+ return 0;
+#endif
+
+ if (! reload_completed)
+ return 0;
+
+ for (regno = reglimit - 1; regno >= 0; regno--)
+ if ((regs_ever_live[regno] && ! call_used_regs[regno])
+ || (current_function_uses_pic_offset_table
+ && regno == PIC_OFFSET_TABLE_REGNUM))
+ nregs++;
+
+ return nregs == 0 || ! frame_pointer_needed;
+}
+
+/* This function generates the assembly code for function exit.
+ FILE is an stdio stream to output the code to.
+ SIZE is an int: how many units of temporary storage to deallocate. */
+
+void
+function_epilogue (file, size)
+ FILE *file;
+ int size;
+{
+ register int regno;
+ register int nregs, limit;
+ int offset;
+ rtx xops[3];
+
+ /* Compute the number of registers to pop */
+
+ 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])
+ || (current_function_uses_pic_offset_table
+ && regno == PIC_OFFSET_TABLE_REGNUM))
+ nregs++;
+
+ /* sp is often unreliable so we must go off the frame pointer,
+ */
+
+ /* In reality, we may not care if sp is unreliable, because we can
+ restore the register relative to the frame pointer. In theory,
+ since each move is the same speed as a pop, and we don't need the
+ leal, this is faster. For now restore multiple registers the old
+ way. */
+
+ offset = -size - (nregs * UNITS_PER_WORD);
+
+ xops[2] = stack_pointer_rtx;
+
+ if (nregs > 1 || ! frame_pointer_needed)
+ {
+ if (frame_pointer_needed)
+ {
+ xops[0] = adj_offsettable_operand (AT_BP (Pmode), offset);
+ output_asm_insn (AS2 (lea%L2,%0,%2), xops);
+ }
+
+ for (regno = 0; regno < limit; regno++)
+ if ((regs_ever_live[regno] && ! call_used_regs[regno])
+ || (current_function_uses_pic_offset_table
+ && regno == PIC_OFFSET_TABLE_REGNUM))
+ {
+ xops[0] = gen_rtx (REG, SImode, regno);
+ output_asm_insn ("pop%L0 %0", xops);
+ }
+ }
+ else
+ for (regno = 0; regno < limit; regno++)
+ if ((regs_ever_live[regno] && ! call_used_regs[regno])
+ || (current_function_uses_pic_offset_table
+ && regno == PIC_OFFSET_TABLE_REGNUM))
+ {
+ xops[0] = gen_rtx (REG, SImode, regno);
+ xops[1] = adj_offsettable_operand (AT_BP (Pmode), offset);
+ output_asm_insn (AS2 (mov%L0,%1,%0), xops);
+ offset += 4;
+ }
+
+ if (frame_pointer_needed)
+ {
+ /* On i486, mov & pop is faster than "leave". */
+
+ if (TARGET_486)
+ {
+ xops[0] = frame_pointer_rtx;
+ output_asm_insn (AS2 (mov%L2,%0,%2), xops);
+ output_asm_insn ("pop%L0 %0", xops);
+ }
+ else
+ output_asm_insn ("leave", xops);
+ }
+ else if (size)
+ {
+ /* If there is no frame pointer, we must still release the frame. */
+
+ xops[0] = gen_rtx (CONST_INT, VOIDmode, size);
+ output_asm_insn (AS2 (add%L2,%0,%2), xops);
+ }
+
+ if (current_function_pops_args && current_function_args_size)
+ {
+ xops[1] = gen_rtx (CONST_INT, VOIDmode, current_function_pops_args);
+
+ /* i386 can only pop 32K bytes (maybe 64K? Is it signed?). If
+ asked to pop more, pop return address, do explicit add, and jump
+ indirectly to the caller. */
+
+ if (current_function_pops_args >= 32768)
+ {
+ /* ??? Which register to use here? */
+ xops[0] = gen_rtx (REG, SImode, 2);
+ output_asm_insn ("pop%L0 %0", xops);
+ output_asm_insn (AS2 (add%L2,%1,%2), xops);
+ output_asm_insn ("jmp %*%0", xops);
+ }
+ else
+ output_asm_insn ("ret %1", xops);
+ }
+ else if (current_function_returns_struct)
+ {
+ xops[0] = gen_rtx (CONST_INT, VOIDmode, 4);
+ output_asm_insn ("ret %0", xops);
+ }
+ else
+ output_asm_insn ("ret", xops);
+}
+
+/* Print an integer constant expression in assembler syntax. Addition
+ and subtraction are the only arithmetic that may appear in these
+ expressions. FILE is the stdio stream to write to, X is the rtx, and
+ CODE is the operand print code from the output string. */
+
+static void
+output_pic_addr_const (file, x, code)
+ FILE *file;
+ rtx x;
+ int code;
+{
+ char buf[256];
+
+ switch (GET_CODE (x))
+ {
+ case PC:
+ if (flag_pic)
+ putc ('.', file);
+ else
+ abort ();
+ break;
+
+ case SYMBOL_REF:
+ case LABEL_REF:
+ if (GET_CODE (x) == SYMBOL_REF)
+ assemble_name (file, XSTR (x, 0));
+ else
+ {
+ ASM_GENERATE_INTERNAL_LABEL (buf, "L",
+ CODE_LABEL_NUMBER (XEXP (x, 0)));
+ assemble_name (asm_out_file, buf);
+ }
+
+ if (GET_CODE (x) == SYMBOL_REF && CONSTANT_POOL_ADDRESS_P (x))
+ fprintf (file, "@GOTOFF(%%ebx)");
+ else if (code == 'P')
+ fprintf (file, "@PLT");
+ else if (GET_CODE (x) == LABEL_REF || ! SYMBOL_REF_FLAG (x))
+ fprintf (file, "@GOT");
+ else
+ fprintf (file, "@GOTOFF");
+
+ break;
+
+ case CODE_LABEL:
+ ASM_GENERATE_INTERNAL_LABEL (buf, "L", CODE_LABEL_NUMBER (x));
+ assemble_name (asm_out_file, buf);
+ break;
+
+ case CONST_INT:
+ fprintf (file, "%d", INTVAL (x));
+ break;
+
+ case CONST:
+ /* This used to output parentheses around the expression,
+ but that does not work on the 386 (either ATT or BSD assembler). */
+ output_pic_addr_const (file, XEXP (x, 0), code);
+ break;
+
+ case CONST_DOUBLE:
+ if (GET_MODE (x) == VOIDmode)
+ {
+ /* We can use %d if the number is <32 bits and positive. */
+ if (CONST_DOUBLE_HIGH (x) || CONST_DOUBLE_LOW (x) < 0)
+ fprintf (file, "0x%x%08x",
+ CONST_DOUBLE_HIGH (x), CONST_DOUBLE_LOW (x));
+ else
+ fprintf (file, "%d", CONST_DOUBLE_LOW (x));
+ }
+ else
+ /* We can't handle floating point constants;
+ PRINT_OPERAND must handle them. */
+ output_operand_lossage ("floating constant misused");
+ break;
+
+ case PLUS:
+ /* Some assemblers need integer constants to appear last (eg masm). */
+ if (GET_CODE (XEXP (x, 0)) == CONST_INT)
+ {
+ output_pic_addr_const (file, XEXP (x, 1), code);
+ if (INTVAL (XEXP (x, 0)) >= 0)
+ fprintf (file, "+");
+ output_pic_addr_const (file, XEXP (x, 0), code);
+ }
+ else
+ {
+ output_pic_addr_const (file, XEXP (x, 0), code);
+ if (INTVAL (XEXP (x, 1)) >= 0)
+ fprintf (file, "+");
+ output_pic_addr_const (file, XEXP (x, 1), code);
+ }
+ break;
+
+ case MINUS:
+ output_pic_addr_const (file, XEXP (x, 0), code);
+ fprintf (file, "-");
+ output_pic_addr_const (file, XEXP (x, 1), code);
+ break;
+
+ default:
+ output_operand_lossage ("invalid expression as operand");
+ }
+}
+
+/* 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.
+ If CODE is 'h', pretend the reg is the `high' byte register.
+ If CODE is 'y', print "st(0)" instead of "st", if the reg is stack op. */
+
+#define PRINT_REG(X, CODE, FILE) \
+ do { if (REGNO (X) == ARG_POINTER_REGNUM) \
+ abort (); \
+ fprintf (FILE, "%s", RP); \
+ switch ((CODE == 'w' ? 2 \
+ : CODE == 'b' ? 1 \
+ : CODE == 'k' ? 4 \
+ : CODE == 'y' ? 3 \
+ : CODE == 'h' ? 0 \
+ : GET_MODE_SIZE (GET_MODE (X)))) \
+ { \
+ case 3: \
+ if (STACK_TOP_P (X)) \
+ { \
+ fputs ("st(0)", FILE); \
+ break; \
+ } \
+ 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; \
+ case 0: \
+ fputs (qi_high_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).
+ D,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 'D':
+ PUT_OP_SIZE (code, 'l', file);
+ 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':
+ /* 387 opcodes don't get size suffixes if the operands are
+ registers. */
+
+ if (STACK_REG_P (x))
+ return;
+
+ /* this is the size of op from size of operand */
+ switch (GET_MODE_SIZE (GET_MODE (x)))
+ {
+ case 1:
+ PUT_OP_SIZE ('B', 'b', file);
+ return;
+
+ 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 (GET_MODE_CLASS (GET_MODE (x)) == MODE_INT)
+ PUT_OP_SIZE ('Q', 'l', file);
+
+ PUT_OP_SIZE ('Q', 'l', 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)))
+ {
+ if (flag_pic)
+ output_pic_addr_const (file, XEXP (x, 0), code);
+ else
+ 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' && code != 'P')
+ {
+ 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);
+ }
+ if (flag_pic)
+ output_pic_addr_const (file, x, code);
+ else
+ 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
+ {
+ if (flag_pic)
+ output_pic_addr_const (file, addr, 0);
+ else
+ output_addr_const (file, addr);
+ }
+ }
+
+ if (ireg != 0 && GET_CODE (ireg) == MULT)
+ {
+ scale = INTVAL (XEXP (ireg, 1));
+ ireg = XEXP (ireg, 0);
+ }
+
+ /* The stack pointer can only appear as a base register,
+ never an index register, so exchange the regs if it is wrong. */
+
+ if (scale == 1 && ireg && REGNO (ireg) == STACK_POINTER_REGNUM)
+ {
+ rtx tmp;
+
+ tmp = breg;
+ breg = ireg;
+ ireg = tmp;
+ }
+
+ /* 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
+ {
+ if (flag_pic)
+ output_pic_addr_const (file, addr, 0);
+ 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. */
+
+void
+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;
+ }
+}
+
+/* Split one or more DImode RTL references into pairs of SImode
+ references. The RTL can be REG, offsettable MEM, integer constant, or
+ CONST_DOUBLE. "operands" is a pointer to an array of DImode RTL to
+ split and "num" is its length. lo_half and hi_half are output arrays
+ that parallel "operands". */
+
+void
+split_di (operands, num, lo_half, hi_half)
+ rtx operands[];
+ int num;
+ rtx lo_half[], hi_half[];
+{
+ while (num--)
+ {
+ if (GET_CODE (operands[num]) == REG)
+ {
+ lo_half[num] = gen_rtx (REG, SImode, REGNO (operands[num]));
+ hi_half[num] = gen_rtx (REG, SImode, REGNO (operands[num]) + 1);
+ }
+ else if (CONSTANT_P (operands[num]))
+ {
+ split_double (operands[num], &lo_half[num], &hi_half[num]);
+ }
+ else if (offsettable_memref_p (operands[num]))
+ {
+ lo_half[num] = operands[num];
+ hi_half[num] = adj_offsettable_operand (operands[num], 4);
+ }
+ else
+ abort();
+ }
+}
+
+/* Return 1 if this is a valid binary operation on a 387.
+ OP is the expression matched, and MODE is its mode. */
+
+int
+binary_387_op (op, mode)
+ register rtx op;
+ enum machine_mode mode;
+{
+ if (mode != VOIDmode && mode != GET_MODE (op))
+ return 0;
+
+ switch (GET_CODE (op))
+ {
+ case PLUS:
+ case MINUS:
+ case MULT:
+ case DIV:
+ return GET_MODE_CLASS (GET_MODE (op)) == MODE_FLOAT;
+
+ default:
+ return 0;
+ }
+}
+
+/* Return 1 if this is a valid conversion operation on a 387.
+ OP is the expression matched, and MODE is its mode. */
+
+int
+convert_387_op (op, mode)
+ register rtx op;
+ enum machine_mode mode;
+{
+ if (mode != VOIDmode && mode != GET_MODE (op))
+ return 0;
+
+ switch (GET_CODE (op))
+ {
+ case FLOAT:
+ return GET_MODE (XEXP (op, 0)) == SImode;
+
+ case FLOAT_EXTEND:
+ return mode == DFmode && GET_MODE (XEXP (op, 0)) == SFmode;
+
+ default:
+ return 0;
+ }
+}
+
+/* Return 1 if this is a valid "float from int" operation on a 387.
+ OP is the expression matched, and MODE is its mode. */
+
+int
+float_op (op, mode)
+ register rtx op;
+ enum machine_mode mode;
+{
+ if (mode != VOIDmode && mode != GET_MODE (op))
+ return 0;
+
+ return GET_CODE (op) == FLOAT
+ && GET_MODE_CLASS (GET_MODE (op)) == MODE_FLOAT;
+}
+
+/* Return 1 if this is a valid shift or rotate operation on a 386.
+ OP is the expression matched, and MODE is its mode. */
+
+int
+shift_op (op, mode)
+ register rtx op;
+ enum machine_mode mode;
+{
+ rtx operand = XEXP (op, 0);
+
+ if (mode != VOIDmode && mode != GET_MODE (op))
+ return 0;
+
+ if (GET_MODE (operand) != GET_MODE (op)
+ || GET_MODE_CLASS (GET_MODE (op)) != MODE_INT)
+ return 0;
+
+ return (GET_CODE (op) == ASHIFT
+ || GET_CODE (op) == ASHIFTRT
+ || GET_CODE (op) == LSHIFTRT
+ || GET_CODE (op) == ROTATE
+ || GET_CODE (op) == ROTATERT);
+}
+
+/* Output code to perform a 387 binary operation in INSN, one of PLUS,
+ MINUS, MULT or DIV. OPERANDS are the insn operands, where operands[3]
+ is the expression of the binary operation. The output may either be
+ emitted here, or returned to the caller, like all output_* functions.
+
+ There is no guarantee that the operands are the same mode, as they
+ might be within FLOAT or FLOAT_EXTEND expressions. */
+
+char *
+output_387_binary_op (insn, operands)
+ rtx insn;
+ rtx *operands;
+{
+ rtx temp;
+ char *base_op;
+ static char buf[100];
+
+ switch (GET_CODE (operands[3]))
+ {
+ case PLUS:
+ if (GET_MODE_CLASS (GET_MODE (operands[1])) == MODE_INT
+ || GET_MODE_CLASS (GET_MODE (operands[2])) == MODE_INT)
+ base_op = "fiadd";
+ else
+ base_op = "fadd";
+ break;
+
+ case MINUS:
+ if (GET_MODE_CLASS (GET_MODE (operands[1])) == MODE_INT
+ || GET_MODE_CLASS (GET_MODE (operands[2])) == MODE_INT)
+ base_op = "fisub";
+ else
+ base_op = "fsub";
+ break;
+
+ case MULT:
+ if (GET_MODE_CLASS (GET_MODE (operands[1])) == MODE_INT
+ || GET_MODE_CLASS (GET_MODE (operands[2])) == MODE_INT)
+ base_op = "fimul";
+ else
+ base_op = "fmul";
+ break;
+
+ case DIV:
+ if (GET_MODE_CLASS (GET_MODE (operands[1])) == MODE_INT
+ || GET_MODE_CLASS (GET_MODE (operands[2])) == MODE_INT)
+ base_op = "fidiv";
+ else
+ base_op = "fdiv";
+ break;
+
+ default:
+ abort ();
+ }
+
+ strcpy (buf, base_op);
+
+ switch (GET_CODE (operands[3]))
+ {
+ case MULT:
+ case PLUS:
+ if (REG_P (operands[2]) && REGNO (operands[0]) == REGNO (operands[2]))
+ {
+ temp = operands[2];
+ operands[2] = operands[1];
+ operands[1] = temp;
+ }
+
+ if (GET_CODE (operands[2]) == MEM)
+ return strcat (buf, AS1 (%z2,%2));
+
+ if (NON_STACK_REG_P (operands[1]))
+ {
+ output_op_from_reg (operands[1], strcat (buf, AS1 (%z0,%1)));
+ RET;
+ }
+ else if (NON_STACK_REG_P (operands[2]))
+ {
+ output_op_from_reg (operands[2], strcat (buf, AS1 (%z0,%1)));
+ RET;
+ }
+
+ if (find_regno_note (insn, REG_DEAD, REGNO (operands[2])))
+ return strcat (buf, AS2 (p,%2,%0));
+
+ if (STACK_TOP_P (operands[0]))
+ return strcat (buf, AS2 (,%y2,%0));
+ else
+ return strcat (buf, AS2 (,%2,%0));
+
+ case MINUS:
+ case DIV:
+ if (GET_CODE (operands[1]) == MEM)
+ return strcat (buf, AS1 (r%z1,%1));
+
+ if (GET_CODE (operands[2]) == MEM)
+ return strcat (buf, AS1 (%z2,%2));
+
+ if (NON_STACK_REG_P (operands[1]))
+ {
+ output_op_from_reg (operands[1], strcat (buf, AS1 (r%z0,%1)));
+ RET;
+ }
+ else if (NON_STACK_REG_P (operands[2]))
+ {
+ output_op_from_reg (operands[2], strcat (buf, AS1 (%z0,%1)));
+ RET;
+ }
+
+ if (! STACK_REG_P (operands[1]) || ! STACK_REG_P (operands[2]))
+ abort ();
+
+ if (find_regno_note (insn, REG_DEAD, REGNO (operands[2])))
+ return strcat (buf, AS2 (rp,%2,%0));
+
+ if (find_regno_note (insn, REG_DEAD, REGNO (operands[1])))
+ return strcat (buf, AS2 (p,%1,%0));
+
+ if (STACK_TOP_P (operands[0]))
+ {
+ if (STACK_TOP_P (operands[1]))
+ return strcat (buf, AS2 (,%y2,%0));
+ else
+ return strcat (buf, AS2 (r,%y1,%0));
+ }
+ else if (STACK_TOP_P (operands[1]))
+ return strcat (buf, AS2 (,%1,%0));
+ else
+ return strcat (buf, AS2 (r,%2,%0));
+
+ default:
+ abort ();
+ }
+}
+
+/* Output code for INSN to convert a float to a signed int. OPERANDS
+ are the insn operands. The output may be SFmode or DFmode and the
+ input operand may be SImode or DImode. As a special case, make sure
+ that the 387 stack top dies if the output mode is DImode, because the
+ hardware requires this. */
+
+char *
+output_fix_trunc (insn, operands)
+ rtx insn;
+ rtx *operands;
+{
+ int stack_top_dies = find_regno_note (insn, REG_DEAD, FIRST_STACK_REG) != 0;
+ rtx xops[6];
+
+ if (! STACK_TOP_P (operands[1]) ||
+ (GET_MODE (operands[0]) == DImode && ! stack_top_dies))
+ abort ();
+
+ xops[0] = stack_pointer_rtx;
+ xops[1] = AT_SP (SImode);
+ xops[2] = adj_offsettable_operand (xops[1], 2);
+ xops[3] = gen_rtx (CONST_INT, VOIDmode, 4);
+ xops[4] = gen_rtx (CONST_INT, VOIDmode, 0xc00);
+ xops[5] = operands[2];
+
+ output_asm_insn (AS2 (sub%L0,%3,%0), xops);
+ output_asm_insn (AS1 (fnstc%W5,%1), xops);
+ output_asm_insn (AS2 (mov%W5,%1,%5), xops);
+ output_asm_insn (AS2 (or%W5,%4,%5), xops);
+ output_asm_insn (AS2 (mov%W5,%5,%2), xops);
+ output_asm_insn (AS1 (fldc%W5,%2), xops);
+
+ if (NON_STACK_REG_P (operands[0]))
+ output_to_reg (operands[0], stack_top_dies);
+ else if (GET_CODE (operands[0]) == MEM)
+ {
+ /* If frame pointer elimination is being done, the MEM reference
+ might be an index off of the stack pointer. In that case,
+ since we have already adjusted %esp above, adjust the operand
+ address so it points where it should. */
+
+ if (! frame_pointer_needed
+ && reg_mentioned_p (stack_pointer_rtx, operands[0]))
+ operands[0] = adj_offsettable_operand (operands[0], 4);
+
+ if (stack_top_dies)
+ output_asm_insn (AS1 (fistp%z0,%0), operands);
+ else
+ output_asm_insn (AS1 (fist%z0,%0), operands);
+ }
+ else
+ abort ();
+
+ output_asm_insn (AS1 (fldc%W5,%1), xops);
+ output_asm_insn (AS2 (add%L0,%3,%0), xops);
+
+ RET;
+}
+
+/* Output code for INSN to compare OPERANDS. The two operands might
+ not have the same mode: one might be within a FLOAT or FLOAT_EXTEND
+ expression. */
+
+char *
+output_float_compare (insn, operands)
+ rtx insn;
+ rtx *operands;
+{
+ int stack_top_dies;
+
+ if (! STACK_TOP_P (operands[0]))
+ abort ();
+
+ stack_top_dies = find_regno_note (insn, REG_DEAD, FIRST_STACK_REG) != 0;
+
+ if (STACK_REG_P (operands[1])
+ && stack_top_dies
+ && find_regno_note (insn, REG_DEAD, REGNO (operands[1]))
+ && REGNO (operands[1]) != FIRST_STACK_REG)
+ {
+ /* If both the top of the 387 stack dies, and the other operand
+ is also a stack register that dies, then this must be a
+ `fcompp' float compare */
+
+ output_asm_insn ("fcompp", operands);
+ }
+ else
+ {
+ static char buf[100];
+
+ /* Decide if this is the integer or float compare opcode. */
+
+ if (GET_MODE_CLASS (GET_MODE (operands[1])) == MODE_FLOAT)
+ strcpy (buf, "fcom");
+ else
+ strcpy (buf, "ficom");
+
+ /* Modify the opcode if the 387 stack is to be popped. */
+
+ if (stack_top_dies)
+ strcat (buf, "p");
+
+ if (NON_STACK_REG_P (operands[1]))
+ output_op_from_reg (operands[1], strcat (buf, AS1 (%z0,%1)));
+ else
+ output_asm_insn (strcat (buf, AS1 (%z1,%y1)), operands);
+ }
+
+ /* Now retrieve the condition code. */
+
+ output_asm_insn (AS1 (fnsts%W2,%2), operands);
+
+ cc_status.flags |= CC_IN_80387;
+ return "sahf";
+}
+
+#ifdef HANDLE_PRAGMA
+
+/* When structure field packing is in effect, this variable is the
+ number of bits to use as the maximum alignment. When packing is not
+ in effect, this is zero. */
+
+int maximum_field_alignment = 0;
+
+/* Handle a pragma directive. HANDLE_PRAGMA conspires to parse the
+ input following #pragma into tokens based on yylex. TOKEN is the
+ current token, and STRING is its printable form. */
+
+void
+handle_pragma_token (string, token)
+ char *string;
+ tree token;
+{
+ static enum pragma_state
+ {
+ ps_start,
+ ps_done,
+ ps_bad,
+ ps_weak,
+ ps_name,
+ ps_equals,
+ ps_value,
+ ps_pack,
+ ps_left,
+ ps_align,
+ ps_right
+ } state = ps_start, type;
+ static char *name;
+ static char *value;
+ static int align;
+
+ if (string == 0)
+ {
+ if (type == ps_pack)
+ {
+ if (state == ps_right)
+ maximum_field_alignment = align * 8;
+ else
+ warning ("ignoring malformed #pragma pack( [ 1 | 2 | 4 ] )");
+ }
+#ifdef WEAK_ASM_OP
+ else if (type == ps_weak)
+ {
+ if (state == ps_name || state == ps_value)
+ {
+ fprintf (asm_out_file, "\t%s\t", WEAK_ASM_OP);
+ ASM_OUTPUT_LABELREF (asm_out_file, name);
+ fputc ('\n', asm_out_file);
+ if (state == ps_value)
+ {
+ fprintf (asm_out_file, "\t%s\t", DEF_ASM_OP);
+ ASM_OUTPUT_LABELREF (asm_out_file, name);
+ fputc (',', asm_out_file);
+ ASM_OUTPUT_LABELREF (asm_out_file, value);
+ fputc ('\n', asm_out_file);
+ }
+ }
+ else if (! (state == ps_done || state == ps_start))
+ warning ("ignoring malformed #pragma weak symbol [=value]");
+ }
+#endif /* WEAK_ASM_OP */
+
+ type = state = ps_start;
+ return;
+ }
+
+ switch (state)
+ {
+ case ps_start:
+ if (token && TREE_CODE (token) == IDENTIFIER_NODE)
+ {
+ if (strcmp (IDENTIFIER_POINTER (token), "pack") == 0)
+ type = state = ps_pack;
+#ifdef WEAK_ASM_OP
+ else if (strcmp (IDENTIFIER_POINTER (token), "weak") == 0)
+ type = state = ps_weak;
+#endif
+ else
+ type = state = ps_done;
+ }
+ else
+ type = state = ps_done;
+ break;
+
+#ifdef WEAK_ASM_OP
+ case ps_weak:
+ if (token && TREE_CODE (token) == IDENTIFIER_NODE)
+ {
+ name = IDENTIFIER_POINTER (token);
+ state = ps_name;
+ }
+ else
+ state = ps_bad;
+ break;
+
+ case ps_name:
+ state = (strcmp (string, "=") ? ps_bad : ps_equals);
+ break;
+
+ case ps_equals:
+ if (token && TREE_CODE (token) == IDENTIFIER_NODE)
+ {
+ value = IDENTIFIER_POINTER (token);
+ state = ps_value;
+ }
+ else
+ state = ps_bad;
+ break;
+
+ case ps_value:
+ state = ps_bad;
+ break;
+#endif /* WEAK_ASM_OP */
+
+ case ps_pack:
+ if (strcmp (string, "(") == 0)
+ state = ps_left;
+ else
+ state = ps_bad;
+ break;
+
+ case ps_left:
+ if (token && TREE_CODE (token) == INTEGER_CST
+ && TREE_INT_CST_HIGH (token) == 0)
+ switch (TREE_INT_CST_LOW (token))
+ {
+ case 1:
+ case 2:
+ case 4:
+ align = TREE_INT_CST_LOW (token);
+ state = ps_align;
+ break;
+
+ default:
+ state = ps_bad;
+ }
+ else if (! token && strcmp (string, ")") == 0)
+ {
+ align = 0;
+ state = ps_right;
+ }
+ else
+ state = ps_bad;
+ break;
+
+ case ps_align:
+ if (strcmp (string, ")") == 0)
+ state = ps_right;
+ else
+ state = ps_bad;
+ break;
+
+ case ps_right:
+ state = ps_bad;
+ break;
+
+ case ps_bad:
+ case ps_done:
+ break;
+
+ default:
+ abort ();
+ }
+}
+#endif /* HANDLE_PRAGMA */
diff --git a/gcc/config/svr4.h b/gcc/config/svr4.h
new file mode 100644
index 00000000000..7db294c051a
--- /dev/null
+++ b/gcc/config/svr4.h
@@ -0,0 +1,667 @@
+/* svr4.h -- operating system specific defines to be used when
+ targeting GCC for some generic System V Release 4 system.
+ Copyright (C) 1991 Free Software Foundation, Inc.
+
+ Written by Ron Guilmette (rfg@ncd.com).
+
+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.
+
+ To use this file, make up a file with a name like:
+
+ ?????svr4.h
+
+ where ????? is replaced by the name of the basic hardware that you
+ are targeting for. Then, in the file ?????svr4.h, put something
+ like:
+
+ #include "?????.h"
+ #include "svr4.h"
+
+ followed by any really system-specific defines (or overrides of
+ defines) which you find that you need. For example, CPP_PREDEFINES
+ is defined here with only the defined -Dunix and -DSVR4. You should
+ probably override that in your target-specific ?????svr4.h file
+ with a set of defines that includes these, but also contains an
+ appropriate define for the type of hardware that you are targeting.
+*/
+
+/* Define a symbol so that libgcc* can know what sort of operating
+ environment and assembler syntax we are targeting for. */
+#ifndef SVR4
+#define SVR4
+#endif
+
+/* For the sake of libgcc2.c, indicate target supports atexit. */
+#define HAVE_ATEXIT
+
+/* Cpp, assembler, linker, library, and startfile spec's. */
+
+/* This defines which switch letters take arguments. On svr4, most of
+ the normal cases (defined in gcc.c) apply, and we also have -h* and
+ -z* options (for the linker). Note however that there is no such
+ thing as a -T option for svr4. */
+
+#define SWITCH_TAKES_ARG(CHAR) \
+ ( (CHAR) == 'D' \
+ || (CHAR) == 'U' \
+ || (CHAR) == 'o' \
+ || (CHAR) == 'e' \
+ || (CHAR) == 'u' \
+ || (CHAR) == 'I' \
+ || (CHAR) == 'm' \
+ || (CHAR) == 'L' \
+ || (CHAR) == 'A' \
+ || (CHAR) == 'h' \
+ || (CHAR) == 'z')
+
+/* This defines which multi-letter switches take arguments. On svr4,
+ there are no such switches except those implemented by GCC itself. */
+
+#define WORD_SWITCH_TAKES_ARG(STR) \
+ (!strcmp (STR, "include") || !strcmp (STR, "imacros"))
+
+/* You should redefine CPP_PREDEFINES in any file which includes this one.
+ The definition should be appropriate for the type of target system
+ involved, and it should include any -A (assertion) options which are
+ appropriate for the given target system. */
+#undef CPP_PREDEFINES
+
+/* Provide an ASM_SPEC appropriate for svr4. Here we try to support as
+ many of the specialized svr4 assembler options as seems reasonable,
+ given that there are certain options which we can't (or shouldn't)
+ support directly due to the fact that they conflict with other options
+ for other svr4 tools (e.g. ld) or with other options for GCC itself.
+ For example, we don't support the -o (output file) or -R (remove
+ input file) options because GCC already handles these things. We
+ also don't support the -m (run m4) option for the assembler because
+ that conflicts with the -m (produce load map) option of the svr4
+ linker. We do however allow passing arbitrary options to the svr4
+ assembler via the -Wa, option.
+
+ Note that gcc doesn't allow a space to follow -Y in a -Ym,* or -Yd,*
+ option.
+*/
+
+#undef ASM_SPEC
+#define ASM_SPEC \
+ "%{V} %{v:%{!V:-V}} %{Qy:} %{!Qn:-Qy} %{n} %{T} %{Ym,*} %{Yd,*} %{Wa,*:%*}"
+
+/* svr4 assemblers need the `-' (indicating input from stdin) to come after
+ the -o option (and its argument) for some reason. If we try to put it
+ before the -o option, the assembler will try to read the file named as
+ the output file in the -o option as an input file (after it has already
+ written some stuff to it) and the binary stuff contained therein will
+ cause totally confuse the assembler, resulting in many spurious error
+ messages. */
+
+#undef ASM_FINAL_SPEC
+#define ASM_FINAL_SPEC "%{pipe:-}"
+
+/* Under svr4, the normal location of the various *crt*.o files is the
+ /usr/ccs/lib directory. */
+
+#undef MD_STARTFILE_PREFIX
+#define MD_STARTFILE_PREFIX "/usr/ccs/lib/"
+
+/* Provide a LIB_SPEC appropropriate for svr4. Here we tack on the default
+ standard C library (unless we are building a shared library) followed by
+ our own magical crtend.o file (see crtstuff.c) which provides part of
+ the support for getting C++ file-scope static object constructed before
+ entering `main', followed by the normal svr3/svr4 "finalizer" file,
+ which is either `gcrtn.o' or `crtn.o'. */
+
+#undef LIB_SPEC
+#define LIB_SPEC \
+ "%{!shared:%{!symbolic:-lc}} \
+ crtend.o%s \
+ %{!shared:%{!symbolic:%{pg:gcrtn.o}%{!pg:crtn.o%s}}}"
+
+/* Provide a LINK_SPEC appropriate for svr4. Here we provide support
+ for the special GCC options -static, -shared, and -symbolic which
+ allow us to link things in one of these three modes by applying the
+ appropriate combinations of options at link-time. We also provide
+ support here for as many of the other svr4 linker options as seems
+ reasonable, given that some of them conflict with options for other
+ svr4 tools (e.g. the assembler). In particular, we do support the
+ -h*, -z*, -V, -b, -t, -Qy, -Qn, and -YP* options here, and the -e*,
+ -l*, -o*, -r, -s, -u*, and -L* options are directly supported
+ by gcc.c itself. We don't directly support the -m (generate load
+ map) option because that conflicts with the -m (run m4) option of
+ the svr4 assembler. We also don't directly support the svr4 linker's
+ -I* or -M* options because these conflict with existing GCC options.
+ We do however allow passing arbitrary options to the svr4 linker
+ via the -Wl, option. We don't support the svr4 linker's -a option
+ at all because it is totally useless and because it conflicts with
+ GCC's own -a option.
+
+ Note that gcc doesn't allow a space to follow -Y in a -YP,* option.
+
+ When the -G link option is used (-shared and -symbolic) a final link is
+ not being done. */
+
+#undef LINK_SPEC
+#define LINK_SPEC "%{z*} %{h*} %{V} %{v:%{!V:-V}} \
+ %{b} %{t} %{Wl,*:%*} \
+ %{static:-dn -Bstatic} \
+ %{shared:-G -dy} \
+ %{symbolic:-Bsymbolic -G -dy} \
+ %{YP,*} \
+ %{!YP,*:%{p:-Y P,/usr/ccs/lib/libp:/usr/lib/libp:/usr/ccs/lib:/usr/lib} \
+ %{!p:-Y P,/usr/ccs/lib:/usr/lib}} \
+ %{Qy:} %{!Qn:-Qy}"
+
+/* Gcc automatically adds in one of the files /usr/ccs/lib/values-Xc.o,
+ /usr/ccs/lib/values-Xa.o, or /usr/ccs/lib/values-Xt.o for each final
+ link step (depending upon the other gcc options selected, such as
+ -traditional and -ansi). These files each contain one (initialized)
+ copy of a special variable called `_lib_version'. Each one of these
+ files has `_lib_version' initialized to a different (enum) value.
+ The SVR4 library routines query the value of `_lib_version' at run
+ to decide how they should behave. Specifically, they decide (based
+ upon the value of `_lib_version') if they will act in a strictly ANSI
+ conformant manner or not.
+*/
+
+#undef STARTFILE_SPEC
+#define STARTFILE_SPEC "%{!shared: \
+ %{!symbolic: \
+ %{pg:gcrt1.o%s}%{!pg:%{p:mcrt1.o%s}%{!p:crt1.o%s}} \
+ %{pg:gcrti.o%s}%{!pg:crti.o%s} \
+ %{ansi:values-Xc.o%s} \
+ %{!ansi: \
+ %{traditional:values-Xt.o%s} \
+ %{!traditional:values-Xa.o%s}}}} crtbegin.o%s"
+
+/* Attach a sepcial .ident directive to the end of the file to identify
+ the version of GCC which compiled this code. The format of the
+ .ident string is patterened after the ones produced by native svr4
+ C compilers. */
+
+#define ASM_FILE_END(FILE) \
+do { \
+ fprintf ((FILE), "\t.ident\t\"GCC: (GNU) %s\"\n", \
+ version_string); \
+ } while (0)
+
+/* Allow #sccs in preprocessor. */
+
+#define SCCS_DIRECTIVE
+
+/* Output #ident as a .ident. */
+
+#define ASM_OUTPUT_IDENT(FILE, NAME) \
+ fprintf (FILE, "\t.ident \"%s\"\n", NAME);
+
+/* Use periods rather than dollar signs in special g++ assembler names. */
+
+#define NO_DOLLAR_IN_LABEL
+
+/* Writing `int' for a bitfield forces int alignment for the structure. */
+
+#define PCC_BITFIELD_TYPE_MATTERS 1
+
+/* Implicit library calls should use memcpy, not bcopy, etc. */
+
+#define TARGET_MEM_FUNCTIONS
+
+/* System V Release 4 uses DWARF debugging info. */
+
+#define DWARF_DEBUGGING_INFO
+
+/* The numbers used to denote specific machine registers in the System V
+ Release 4 DWARF debugging information are quite likely to be totally
+ different from the numbers used in BSD stabs debugging information
+ for the same kind of target machine. Thus, we undefine the macro
+ DBX_REGISTER_NUMBER here as an extra inducement to get people to
+ provide proper machine-specific definitions of DBX_REGISTER_NUMBER
+ (which is also used to provide DWARF registers numbers in dwarfout.c)
+ in their tm.h files which include this file. */
+
+#undef DBX_REGISTER_NUMBER
+
+/* Define the actual types of some ANSI-mandated types. (These
+ definitions should work for most SVR4 systems). */
+
+#undef SIZE_TYPE
+#define SIZE_TYPE "unsigned int"
+
+#undef PTRDIFF_TYPE
+#define PTRDIFF_TYPE "int"
+
+#undef WCHAR_TYPE
+#define WCHAR_TYPE "long int"
+
+#undef WCHAR_TYPE_SIZE
+#define WCHAR_TYPE_SIZE BITS_PER_WORD
+
+#undef ASM_BYTE_OP
+#define ASM_BYTE_OP "\t.byte"
+
+/* This is how to begin an assembly language file. Most svr4 assemblers want
+ at least a .file directive to come first, and some want to see a .version
+ directive come right after that. Here we just establish a default
+ which generates only the .file directive. If you need a .version
+ directive for any specific target, you should override this definition
+ in the target-specific file which includes this one. */
+
+#undef ASM_FILE_START
+#define ASM_FILE_START(FILE) \
+ output_file_directive ((FILE), main_input_filename)
+
+/* This is how to allocate empty space in some section. The .zero
+ pseudo-op is used for this on most svr4 assemblers. */
+
+#undef ASM_OUTPUT_SKIP
+#define ASM_OUTPUT_SKIP(FILE,SIZE) fprintf (FILE, "\t.zero\t%u\n", (SIZE))
+
+/* This is how to output a reference to a user-level label named NAME.
+ `assemble_name' uses this.
+
+ For System V Release 4 the convention is *not* to prepend a leading
+ underscore onto user-level symbol names. */
+
+#undef ASM_OUTPUT_LABELREF
+#define ASM_OUTPUT_LABELREF(FILE,NAME) fprintf (FILE, "%s", NAME)
+
+/* The standard SVR4 assembler seems to require that certain builtin
+ library routines (e.g. .udiv) be explicitly declared as .globl
+ in each assembly file where they are referenced. */
+
+#define ASM_OUTPUT_EXTERNAL_LIBCALL(FILE, FUN) \
+ ASM_GLOBALIZE_LABEL (FILE, XSTR (FUN, 0))
+
+/* This says how to output assembler code to declare an
+ uninitialized external linkage data object. Under SVR4,
+ the linker seems to want the alignment of data objects
+ to depend on their types. We do exactly that here. */
+
+#undef ASM_OUTPUT_ALIGNED_COMMON
+#define ASM_OUTPUT_ALIGNED_COMMON(FILE, NAME, SIZE, ALIGN) \
+do { \
+ fputs ("\t.comm\t", (FILE)); \
+ assemble_name ((FILE), (NAME)); \
+ fprintf ((FILE), ",%u,%u\n", (SIZE), (ALIGN) / BITS_PER_UNIT); \
+} while (0)
+
+/* This says how to output assembler code to declare an
+ uninitialized internal linkage data object. Under SVR4,
+ the linker seems to want the alignment of data objects
+ to depend on their types. We do exactly that here. */
+
+#define BSS_ASM_OP "\t.bss"
+
+#undef ASM_OUTPUT_ALIGNED_LOCAL
+#define ASM_OUTPUT_ALIGNED_LOCAL(FILE, NAME, SIZE, ALIGN) \
+do { \
+ fprintf ((FILE), "%s\t%s,%u,%u\n", \
+ BSS_ASM_OP, (NAME), (SIZE), (ALIGN) / BITS_PER_UNIT); \
+} while (0)
+
+/* This is the pseudo-op used to generate a 32-bit word of data with a
+ specific value in some section. This is the same for all known svr4
+ assemblers. */
+
+#define INT_ASM_OP "\t.long\t"
+
+/* This is the pseudo-op used to generate a contiguous sequence of byte
+ values from a double-quoted string WITHOUT HAVING A TERMINATING NUL
+ AUTOMATICALLY APPENDED. This is the same for most svr4 assemblers. */
+
+#undef ASCII_DATA_ASM_OP
+#define ASCII_DATA_ASM_OP ".ascii"
+
+/* Support const sections and the ctors and dtors sections for g++.
+ Note that there appears to be two different ways to support const
+ sections at the moment. You can either #define the symbol
+ READONLY_DATA_SECTION (giving it some code which switches to the
+ readonly data section) or else you can #define the symbols
+ EXTRA_SECTIONS, EXTRA_SECTION_FUNCTIONS, SELECT_SECTION, and
+ SELECT_RTX_SECTION. We do both here just to be on the safe side. */
+
+#define USE_CONST_SECTION 1
+
+#define CONST_SECTION_ASM_OP "\t.section\t.rodata"
+#define CTORS_SECTION_ASM_OP "\t.section\t.ctors,\"a\",@progbits\n"
+#define DTORS_SECTION_ASM_OP "\t.section\t.dtors,\"a\",@progbits\n"
+
+/* On svr4, we *do* have support for the .init section, and we can put
+ stuff in there to be executed before `main'. We let crtstuff.c and
+ other files know this by defining the following symbol. The definition
+ says how to change sections to the .init section. This is the same
+ for all know svr4 assemblers. */
+
+#define INIT_SECTION_ASM_OP "\t.section\t.init"
+
+/* A default list of other sections which we might be "in" at any given
+ time. For targets that use additional sections (e.g. .tdesc) you
+ should override this definition in the target-specific file which
+ includes this file. */
+
+#undef EXTRA_SECTIONS
+#define EXTRA_SECTIONS in_const, in_ctors, in_dtors
+
+/* A default list of extra section function definitions. For targets
+ that use additional sections (e.g. .tdesc) you should override this
+ definition in the target-specific file which includes this file. */
+
+#undef EXTRA_SECTION_FUNCTIONS
+#define EXTRA_SECTION_FUNCTIONS \
+ CONST_SECTION_FUNCTION \
+ CTORS_SECTION_FUNCTION \
+ DTORS_SECTION_FUNCTION
+
+#define READONLY_DATA_SECTION() const_section ()
+
+extern void text_section();
+
+#define CONST_SECTION_FUNCTION \
+void \
+const_section () \
+{ \
+ if (!USE_CONST_SECTION) \
+ text_section(); \
+ else if (in_section != in_const) \
+ { \
+ fprintf (asm_out_file, "%s\n", CONST_SECTION_ASM_OP); \
+ in_section = in_const; \
+ } \
+}
+
+#define CTORS_SECTION_FUNCTION \
+void \
+ctors_section () \
+{ \
+ if (in_section != in_ctors) \
+ { \
+ fprintf (asm_out_file, CTORS_SECTION_ASM_OP); \
+ in_section = in_ctors; \
+ } \
+}
+
+#define DTORS_SECTION_FUNCTION \
+void \
+dtors_section () \
+{ \
+ if (in_section != in_dtors) \
+ { \
+ fprintf (asm_out_file, DTORS_SECTION_ASM_OP); \
+ in_section = in_dtors; \
+ } \
+}
+
+/* A C statement (sans semicolon) to output an element in the table of
+ global constructors. */
+#define ASM_OUTPUT_CONSTRUCTOR(FILE,NAME) \
+ do { \
+ ctors_section (); \
+ fprintf (FILE, "%s\t ", INT_ASM_OP); \
+ assemble_name (FILE, NAME); \
+ fprintf (FILE, "\n"); \
+ } while (0)
+
+/* A C statement (sans semicolon) to output an element in the table of
+ global destructors. */
+#define ASM_OUTPUT_DESTRUCTOR(FILE,NAME) \
+ do { \
+ dtors_section (); \
+ fprintf (FILE, "%s\t ", INT_ASM_OP); \
+ assemble_name (FILE, NAME); \
+ fprintf (FILE, "\n"); \
+ } while (0)
+
+/* A C statement or statements to switch to the appropriate
+ section for output of DECL. DECL is either a `VAR_DECL' node
+ or a constant of some sort. RELOC indicates whether forming
+ the initial value of DECL requires link-time relocations. */
+
+#define SELECT_SECTION(DECL,RELOC) \
+{ \
+ if (TREE_CODE (DECL) == STRING_CST) \
+ { \
+ if (! flag_writable_strings) \
+ const_section (); \
+ else \
+ data_section (); \
+ } \
+ else if (TREE_CODE (DECL) == VAR_DECL) \
+ { \
+ if ((flag_pic && RELOC) \
+ || !TREE_READONLY (DECL) || TREE_SIDE_EFFECTS (DECL)) \
+ data_section (); \
+ else \
+ const_section (); \
+ } \
+ else \
+ const_section (); \
+}
+
+/* A C statement or statements to switch to the appropriate
+ section for output of RTX in mode MODE. RTX is some kind
+ of constant in RTL. The argument MODE is redundant except
+ in the case of a `const_int' rtx. Currently, these always
+ go into the const section. */
+
+#undef SELECT_RTX_SECTION
+#define SELECT_RTX_SECTION(MODE,RTX) const_section()
+
+/* Define the strings used for the special svr4 .type and .size directives.
+ These strings generally do not vary from one system running svr4 to
+ another, but if a given system (e.g. m88k running svr) needs to use
+ different pseudo-op names for these, they may be overridden in the
+ file which includes this one. */
+
+#define TYPE_ASM_OP "\t.type"
+#define SIZE_ASM_OP "\t.size"
+
+/* The following macro defines the format used to output the second
+ operand of the .type assembler directive. Different svr4 assemblers
+ expect various different forms for this operand. The one given here
+ is just a default. You may need to override it in your machine-
+ specific tm.h file (depending upon the particulars of your assembler). */
+
+#define TYPE_OPERAND_FMT "@%s"
+
+/* These macros generate the special .type and .size directives which
+ are used to set the corresponding fields of the linker symbol table
+ entries in an ELF object file under SVR4. */
+
+/* Write the extra assembler code needed to declare a function properly. */
+
+#define ASM_DECLARE_FUNCTION_NAME(FILE, NAME, DECL) \
+ do { \
+ fprintf (FILE, "%s\t ", TYPE_ASM_OP); \
+ assemble_name (FILE, NAME); \
+ putc (',', FILE); \
+ fprintf (FILE, TYPE_OPERAND_FMT, "function"); \
+ putc ('\n', FILE); \
+ ASM_OUTPUT_LABEL(FILE, NAME); \
+ } while (0)
+
+/* Write the extra assembler code needed to declare an object properly. */
+
+#define ASM_DECLARE_OBJECT_NAME(FILE, NAME, DECL) \
+ do { \
+ fprintf (FILE, "%s\t ", TYPE_ASM_OP); \
+ assemble_name (FILE, NAME); \
+ putc (',', FILE); \
+ fprintf (FILE, TYPE_OPERAND_FMT, "object"); \
+ putc ('\n', FILE); \
+ if (!flag_inhibit_size_directive) \
+ { \
+ fprintf (FILE, "%s\t ", SIZE_ASM_OP); \
+ assemble_name (FILE, NAME); \
+ fprintf (FILE, ",%d\n", int_size_in_bytes (TREE_TYPE (decl))); \
+ } \
+ ASM_OUTPUT_LABEL(FILE, NAME); \
+ } while (0)
+
+/* This is how to declare the size of a function. */
+
+#define ASM_DECLARE_FUNCTION_SIZE(FILE, FNAME, DECL) \
+ do { \
+ if (!flag_inhibit_size_directive) \
+ { \
+ char label[256]; \
+ static int labelno; \
+ labelno++; \
+ ASM_GENERATE_INTERNAL_LABEL (label, "Lfe", labelno); \
+ ASM_OUTPUT_INTERNAL_LABEL (FILE, "Lfe", labelno); \
+ fprintf (FILE, "%s\t ", SIZE_ASM_OP); \
+ assemble_name (FILE, (FNAME)); \
+ fprintf (FILE, ","); \
+ assemble_name (FILE, label); \
+ fprintf (FILE, "-"); \
+ ASM_OUTPUT_LABELREF (FILE, (FNAME)); \
+ putc ('\n', FILE); \
+ } \
+ } while (0)
+
+/* A table of bytes codes used by the ASM_OUTPUT_ASCII and
+ ASM_OUTPUT_LIMITED_STRING macros. Each byte in the table
+ corresponds to a particular byte value [0..255]. For any
+ given byte value, if the value in the corresponding table
+ position is zero, the given character can be output directly.
+ If the table value is 1, the byte must be output as a \ooo
+ octal escape. If the tables value is anything else, then the
+ byte value should be output as a \ followed by the value
+ in the table. Note that we can use standard UN*X escape
+ sequences for many control characters, but we don't use
+ \a to represent BEL because some svr4 assemblers (e.g. on
+ the i386) don't know about that. */
+
+#define ESCAPES \
+"\1\1\1\1\1\1\1\1btnvfr\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\
+\0\0\"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
+\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\\\0\0\0\
+\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\
+\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\
+\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\
+\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\
+\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1"
+
+/* Some svr4 assemblers have a limit on the number of characters which
+ can appear in the operand of a .string directive. If your assembler
+ has such a limitation, you should define STRING_LIMIT to reflect that
+ limit. Note that at least some svr4 assemblers have a limit on the
+ actual number of bytes in the double-quoted string, and that they
+ count each chanacter in an escape sequence as one byte. Thus, an
+ escape sequence like \377 would count as four bytes.
+
+ If your target assembler doesn't support the .string directive, you
+ should define this to zero.
+*/
+
+#define STRING_LIMIT ((unsigned) 256)
+
+#define STRING_ASM_OP ".string"
+
+/* The routine used to output NUL terminated strings. We use a special
+ version of this for most svr4 targets because doing so makes the
+ generated assembly code more compact (and thus faster to assemble)
+ as well as more readable, especially for targets like the i386
+ (where the only alternative is to output character sequences as
+ comma separated lists of numbers). */
+
+#define ASM_OUTPUT_LIMITED_STRING(FILE, STR) \
+ do \
+ { \
+ register unsigned char *_limited_str = (unsigned char *) (STR); \
+ register unsigned ch; \
+ fprintf ((FILE), "\t%s\t\"", STRING_ASM_OP); \
+ for (; ch = *_limited_str; _limited_str++) \
+ { \
+ register int escape; \
+ switch (escape = ESCAPES[ch]) \
+ { \
+ case 0: \
+ putc (ch, (FILE)); \
+ break; \
+ case 1: \
+ fprintf ((FILE), "\\%03o", ch); \
+ break; \
+ default: \
+ putc ('\\', (FILE)); \
+ putc (escape, (FILE)); \
+ break; \
+ } \
+ } \
+ fprintf ((FILE), "\"\n"); \
+ } \
+ while (0)
+
+/* The routine used to output sequences of byte values. We use a special
+ version of this for most svr4 targets because doing so makes the
+ generated assembly code more compact (and thus faster to assemble)
+ as well as more readable. Note that if we find subparts of the
+ character sequence which end with NUL (and which are shorter than
+ STRING_LIMIT) we output those using ASM_OUTPUT_LIMITED_STRING. */
+
+#undef ASM_OUTPUT_ASCII
+#define ASM_OUTPUT_ASCII(FILE, STR, LENGTH) \
+ do \
+ { \
+ register unsigned char *_ascii_bytes = (unsigned char *) (STR); \
+ register unsigned char *limit = _ascii_bytes + (LENGTH); \
+ register unsigned bytes_in_chunk = 0; \
+ for (; _ascii_bytes < limit; _ascii_bytes++) \
+ { \
+ register unsigned char *p; \
+ if (bytes_in_chunk >= 60) \
+ { \
+ fprintf ((FILE), "\"\n"); \
+ bytes_in_chunk = 0; \
+ } \
+ for (p = _ascii_bytes; p < limit && *p != '\0'; p++) \
+ continue; \
+ if (p < limit && (p - _ascii_bytes) <= STRING_LIMIT) \
+ { \
+ if (bytes_in_chunk > 0) \
+ { \
+ fprintf ((FILE), "\"\n"); \
+ bytes_in_chunk = 0; \
+ } \
+ ASM_OUTPUT_LIMITED_STRING ((FILE), _ascii_bytes); \
+ _ascii_bytes = p; \
+ } \
+ else \
+ { \
+ register int escape; \
+ register unsigned ch; \
+ if (bytes_in_chunk == 0) \
+ fprintf ((FILE), "\t%s\t\"", ASCII_DATA_ASM_OP); \
+ switch (escape = ESCAPES[ch = *_ascii_bytes]) \
+ { \
+ case 0: \
+ putc (ch, (FILE)); \
+ bytes_in_chunk++; \
+ break; \
+ case 1: \
+ fprintf ((FILE), "\\%03o", ch); \
+ bytes_in_chunk += 4; \
+ break; \
+ default: \
+ putc ('\\', (FILE)); \
+ putc (escape, (FILE)); \
+ bytes_in_chunk += 2; \
+ break; \
+ } \
+ } \
+ } \
+ if (bytes_in_chunk > 0) \
+ fprintf ((FILE), "\"\n"); \
+ } \
+ while (0)