From 53d548f5ab7bda50df5534b1c8dd3d635fd6c982 Mon Sep 17 00:00:00 2001 From: jrv Date: Sat, 1 Feb 1992 06:00:10 +0000 Subject: Initial revision git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@264 138bc75d-0d04-0410-961f-82ee72b054a4 --- gcc/config/i386/i386.c | 1874 ++++++++++++++++++++++++++++++++++++++++++++++++ gcc/config/svr4.h | 667 +++++++++++++++++ 2 files changed, 2541 insertions(+) create mode 100644 gcc/config/i386/i386.c create mode 100644 gcc/config/svr4.h 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 +#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) -- cgit v1.2.1