From: Richard Kenner Date: Tue, 14 Jan 1992 23:37:42 +0000 (-0500) Subject: Initial revision X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=ea5e64f938b9cc739fac9647ac36b6e9f2cb6dde;p=gcc.git Initial revision From-SVN: r184 --- diff --git a/gcc/config/a29k/a29k.c b/gcc/config/a29k/a29k.c new file mode 100644 index 00000000000..e9568d547e7 --- /dev/null +++ b/gcc/config/a29k/a29k.c @@ -0,0 +1,1286 @@ +/* Subroutines used for code generation on AMD Am29000. + Copyright (C) 1987, 1988, 1990, 1991 Free Software Foundation, Inc. + Contributed by Richard Kenner (kenner@nyu.edu) + +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 "flags.h" +#include "recog.h" +#include "expr.h" +#include "obstack.h" +#include "tree.h" + +#define min(A,B) ((A) < (B) ? (A) : (B)) + +/* This gives the size in words of the register stack for the current + procedure. */ + +static int a29k_regstack_size; + +/* This points to the last insn of the insn prologue. It is set when + an insn without a filled delay slot is found near the start of the + function. */ + +static char *a29k_last_prologue_insn; + +/* This points to the first insn that will be in the epilogue. It is null if + no epilogue is required. */ + +static char *a29k_first_epilogue_insn; + +/* This is nonzero if a a29k_first_epilogue_insn was put in a delay slot. It + indicates that an intermediate label needs to be written. */ + +static int a29k_first_epilogue_insn_used; + +/* Location to hold the name of the current function. We need this prolog to + contain the tag words prior to the declaration. So the name must be stored + away. */ + +char *a29k_function_name; + +/* Mapping of registers to debug register numbers. The only change is + for the frame pointer and the register numbers used for the incoming + arguments. */ + +int a29k_debug_reg_map[FIRST_PSEUDO_REGISTER]; + +/* Save information from a "cmpxx" operation until the branch or scc is + emitted. */ + +rtx a29k_compare_op0, a29k_compare_op1; +int a29k_compare_fp_p; + +/* Gives names for registers. */ +extern char *reg_names[]; + +/* Returns 1 if OP is a 8-bit constant. */ + +int +cint_8_operand (op, mode) + register rtx op; + enum machine_mode mode; +{ + return GET_CODE (op) == CONST_INT && (INTVAL (op) & 0xffffff00) == 0; +} + +/* Returns 1 if OP is a 16-bit constant. */ + +int +cint_16_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + return GET_CODE (op) == CONST_INT && (INTVAL (op) & 0xffff0000) == 0; +} + +/* Returns 1 if OP cannot be moved in a single insn. */ + +int +long_const_operand (op, mode) + register rtx op; + enum machine_mode mode; +{ + if (! CONSTANT_P (op)) + return 0; + + if (TARGET_29050 && GET_CODE (op) == CONST_INT + && (INTVAL (op) & 0xffff) == 0) + return 0; + + return (GET_CODE (op) != CONST_INT + || ((INTVAL (op) & 0xffff0000) != 0 + && (INTVAL (op) & 0xffff0000) != 0xffff0000 + && INTVAL (op) != 0x80000000)); +} + +/* The following four functions detect constants of 0, 8, 16, and 24 used as + a position in ZERO_EXTRACT operations. They can either be the appropriate + constant integer or a shift (which will be produced by combine). */ + +static int +shift_constant_operand (op, mode, val) + rtx op; + enum machine_mode mode; + int val; +{ + return ((GET_CODE (op) == CONST_INT && INTVAL (op) == val) + || (GET_CODE (op) == ASHIFT + && GET_CODE (XEXP (op, 0)) == CONST_INT + && INTVAL (XEXP (op, 0)) == val / 8 + && GET_CODE (XEXP (op, 1)) == CONST_INT + && INTVAL (XEXP (op, 1)) == 3)); +} + +int +const_0_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + return shift_constant_operand (op, mode, 0); +} + +int +const_8_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + return shift_constant_operand (op, mode, 8); +} + +int +const_16_operand (op, mode) + rtx op; + enum machine_mode; +{ + return shift_constant_operand (op, mode, 16); +} + +int +const_24_operand (op, mode) + rtx op; + enum machine_mode; +{ + return shift_constant_operand (op, mode, 24); +} + +/* Returns 1 if OP is a floating-point constant of the proper mode. */ + +int +float_const_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + return GET_CODE (op) == CONST_DOUBLE && GET_MODE (op) == mode; +} + +/* Returns 1 if OP is a floating-point constant of the proper mode or a + general-purpose register. */ + +int +gen_reg_or_float_constant_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + return float_const_operand (op, mode) || gen_reg_operand (op, mode); +} + +/* Returns 1 if OP is an integer constant of the proper mode or a + general-purpose register. */ + +int +gen_reg_or_integer_constant_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + return ((GET_MODE (op) == VOIDmode + && (GET_CODE (op) == CONST_INT || GET_CODE (op) == CONST_DOUBLE)) + || gen_reg_operand (op, mode)); +} + +/* Returns 1 if OP is a special machine register. */ + +int +spec_reg_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + return GET_MODE (op) == SImode && GET_CODE (op) == REG + && REGNO (op) >= R_BP && REGNO (op) <= R_EXO; +} + +/* Returns 1 if OP is an accumulator register. */ + +int +accum_reg_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + return (GET_CODE (op) == REG + && REGNO (op) >= R_ACC (0) && REGNO (op) <= R_ACC (3)); +} + +/* Returns 1 if OP is a normal data register. */ + +int +gen_reg_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + int regno; + + if (GET_MODE (op) != mode && mode != VOIDmode) + return 0; + + if (GET_CODE (op) == REG) + regno = REGNO (op); + else if (GET_CODE (op) == SUBREG && GET_CODE (SUBREG_REG (op)) == REG) + { + regno = REGNO (SUBREG_REG (op)); + if (regno < FIRST_PSEUDO_REGISTER) + regno += SUBREG_WORD (op); + } + else + return 0; + + return regno >= FIRST_PSEUDO_REGISTER || regno < R_BP; +} + +/* Returns 1 if OP is either an 8-bit constant integer or a general register. + If a register, it must be in the proper mode unless MODE is VOIDmode. */ + +int +srcb_operand (op, mode) + register rtx op; + enum machine_mode mode; +{ + if (GET_CODE (op) == CONST_INT + && (mode == QImode + || (INTVAL (op) & 0xffffff00) == 0)) + return 1; + + if (GET_MODE (op) != mode && mode != VOIDmode) + return 0; + + return gen_reg_operand (op, mode); +} + +/* Return 1 if OP is either an immediate or a general register. This is used + for the input operand of mtsr/mtrsim. */ + +int +gen_reg_or_immediate_operand (op, mode) + rtx op; + enum machine_mode; +{ + return gen_reg_operand (op, mode) || immediate_operand (op, mode); +} + +/* Return 1 if OP can be used as the second operand of and AND insn. This + includes srcb_operand and a constant whose complement fits in 8 bits. */ + +int +and_operand (op, mode) + rtx op; + enum machine_mode; +{ + return (srcb_operand (op, mode) + || (GET_CODE (op) == CONST_INT + && ((unsigned) ((~ INTVAL (op)) & GET_MODE_MASK (mode)) < 256))); +} + +/* Return 1 if OP can be used as the second operand of an ADD insn. + This is the same as above, except we use negative, rather than + complement. */ + +int +add_operand (op, mode) + rtx op; + enum machine_mode; +{ + return (srcb_operand (op, mode) + || (GET_CODE (op) == CONST_INT + && ((unsigned) ((- INTVAL (op)) & GET_MODE_MASK (mode)) < 256))); +} + +/* Return 1 if OP can be used as the input operand for a move insn. */ + +int +in_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + rtx orig_op = op; + + if (! general_operand (op, mode)) + return 0; + + while (GET_CODE (op) == SUBREG) + op = SUBREG_REG (op); + + switch (GET_CODE (op)) + { + case REG: + return 1; + + case MEM: + return (GET_MODE_SIZE (mode) >= UNITS_PER_WORD || TARGET_DW_ENABLE); + + case CONST_INT: + if (GET_MODE_CLASS (mode) != MODE_INT) + return 0; + + return 1; + + case CONST: + case SYMBOL_REF: + case LABEL_REF: + return (GET_MODE (op) == mode + || mode == SImode || mode == HImode || mode == QImode); + + case CONST_DOUBLE: + return ((GET_MODE_CLASS (mode) == MODE_FLOAT + && mode == GET_MODE (op)) + || (GET_MODE (op) == VOIDmode + && GET_MODE_CLASS (mode) == MODE_INT)); + + default: + return 0; + } +} + +/* Return 1 if OP can be used as the output operand for a move insn. */ + +int +out_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + rtx orig_op = op; + + if (! general_operand (op, mode)) + return 0; + + while (GET_CODE (op) == SUBREG) + op = SUBREG_REG (op); + + if (GET_CODE (op) == REG) + return (mode == SImode || gen_reg_operand (orig_op, mode) + || (GET_MODE_CLASS (mode) == MODE_FLOAT + && accum_reg_operand (orig_op, mode))); + + else if (GET_CODE (op) == MEM) + return mode == SImode || mode == SFmode || TARGET_DW_ENABLE; + + else + return 0; +} + +/* Return 1 if OP is some extension operator. */ + +int +extend_operator (op, mode) + rtx op; + enum machine_mode mode; +{ + return ((mode == VOIDmode || GET_MODE (op) == mode) + && (GET_CODE (op) == ZERO_EXTEND || GET_CODE (op) == SIGN_EXTEND)); +} + +/* Return 1 if OP is a comparison operator that we have in floating-point. */ + +int +fp_comparison_operator (op, mode) + rtx op; + enum machine_mode mode; +{ + return ((mode == VOIDmode || mode == GET_MODE (op)) + && (GET_CODE (op) == EQ || GET_CODE (op) == GT || + GET_CODE (op) == GE)); +} + +/* Return 1 if OP is a valid branch comparison. */ + +int +branch_operator (op, mode) + rtx op; + enum machine_mode mode; +{ + return ((mode == VOIDmode || mode == GET_MODE (op)) + && (GET_CODE (op) == GE || GET_CODE (op) == LT)); +} + +/* Return 1 if OP is a load multiple operation. It is known to be a + PARALLEL and the first three sections will be tested. */ + +int +load_multiple_operation (op, mode) + rtx op; + enum machine_mode mode; +{ + int count = XVECLEN (op, 0) - 2; + int dest_regno; + rtx src_addr; + int i; + + /* Perform a quick check so we don't blow up below. */ + if (count <= 1 + || GET_CODE (XVECEXP (op, 0, 0)) != SET + || GET_CODE (SET_DEST (XVECEXP (op, 0, 0))) != REG + || GET_CODE (SET_SRC (XVECEXP (op, 0, 0))) != MEM) + return 0; + + dest_regno = REGNO (SET_DEST (XVECEXP (op, 0, 0))); + src_addr = XEXP (SET_SRC (XVECEXP (op, 0, 0)), 0); + + for (i = 1; i < count; i++) + { + rtx elt = XVECEXP (op, 0, i + 2); + + if (GET_CODE (elt) != SET + || GET_CODE (SET_DEST (elt)) != REG + || GET_MODE (SET_DEST (elt)) != SImode + || REGNO (SET_DEST (elt)) != dest_regno + i + || GET_CODE (SET_SRC (elt)) != MEM + || GET_MODE (SET_SRC (elt)) != SImode + || GET_CODE (XEXP (SET_SRC (elt), 0)) != PLUS + || ! rtx_equal_p (XEXP (XEXP (SET_SRC (elt), 0), 0), src_addr) + || GET_CODE (XEXP (XEXP (SET_SRC (elt), 0), 1)) != CONST_INT + || INTVAL (XEXP (XEXP (SET_SRC (elt), 0), 1)) != i * 4) + return 0; + } + + return 1; +} + +/* Similar, but tests for store multiple. */ + +int +store_multiple_operation (op, mode) + rtx op; + enum machine_mode mode; +{ + int num_special = TARGET_NO_STOREM_BUG ? 2 : 1; + int count = XVECLEN (op, 0) - num_special; + int src_regno; + rtx dest_addr; + int i; + + /* Perform a quick check so we don't blow up below. */ + if (count <= 1 + || GET_CODE (XVECEXP (op, 0, 0)) != SET + || GET_CODE (SET_DEST (XVECEXP (op, 0, 0))) != MEM + || GET_CODE (SET_SRC (XVECEXP (op, 0, 0))) != REG) + return 0; + + src_regno = REGNO (SET_SRC (XVECEXP (op, 0, 0))); + dest_addr = XEXP (SET_DEST (XVECEXP (op, 0, 0)), 0); + + for (i = 1; i < count; i++) + { + rtx elt = XVECEXP (op, 0, i + num_special); + + if (GET_CODE (elt) != SET + || GET_CODE (SET_SRC (elt)) != REG + || GET_MODE (SET_SRC (elt)) != SImode + || REGNO (SET_SRC (elt)) != src_regno + i + || GET_CODE (SET_DEST (elt)) != MEM + || GET_MODE (SET_DEST (elt)) != SImode + || GET_CODE (XEXP (SET_DEST (elt), 0)) != PLUS + || ! rtx_equal_p (XEXP (XEXP (SET_DEST (elt), 0), 0), dest_addr) + || GET_CODE (XEXP (XEXP (SET_DEST (elt), 0), 1)) != CONST_INT + || INTVAL (XEXP (XEXP (SET_DEST (elt), 0), 1)) != i * 4) + return 0; + } + + return 1; +} + +/* Given a special register REG and MASK, a value being masked against a + quantity to which the special register is set, return 1 if the masking + operation is built-in to the setting of that special register. */ + +int +masks_bits_for_special (reg, mask) + rtx reg; + rtx mask; +{ + int needed_mask_value; + + if (GET_CODE (reg) != REG || GET_CODE (mask) != CONST_INT) + abort (); + + switch (REGNO (reg)) + { + case R_BP: + case R_INT: + needed_mask_value = 3; + break; + + case R_FC: + needed_mask_value = 31; + break; + + case R_CR: + case R_LRU: + needed_mask_value = 255; + break; + + case R_FPE: + needed_mask_value = 511; + break; + + case R_MMU: + needed_mask_value = 0x3ff; + break; + + case R_OPS: + case R_CPS: + case R_RBP: + case R_FPS: + needed_mask_value = 0xffff; + break; + + case R_VAB: + needed_mask_value = 0xffff0000; + break; + + case R_Q: + case R_CFG: + case R_CHA: + case R_CHD: + case R_CHC: + case R_TMC: + case R_TMR: + case R_PC0: + case R_PC1: + case R_PC2: + return 0; + + default: + abort (); + } + + return (INTVAL (mask) & ~ needed_mask_value) == 0; +} + +/* Return nonzero if this label is that of the return point, but there is + a non-null epilogue. */ + +int +epilogue_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + return next_active_insn (op) == 0 && a29k_first_epilogue_insn != 0; +} + +/* Return the register class of a scratch register needed to copy IN into + or out of a register in CLASS in MODE. If it can be done directly, + NO_REGS is returned. */ + +enum reg_class +secondary_reload_class (class, mode, in) + enum reg_class class; + enum machine_mode mode; + rtx in; +{ + int regno = -1; + + if (GET_CODE (in) == REG || GET_CODE (in) == SUBREG) + regno = true_regnum (in); + + /* We can place anything into GENERAL_REGS and can put GENERAL_REGS + into anything. */ + if (class == GENERAL_REGS || (regno != -1 && regno < R_BP)) + return NO_REGS; + + /* We can place 16-bit constants into a special register. */ + if (GET_CODE (in) == CONST_INT + && (GET_MODE_BITSIZE (mode) <= 16 + || (unsigned) INTVAL (in) <= 65535) + && (class == BP_REGS || class == Q_REGS || class == SPECIAL_REGS)) + return NO_REGS; + + /* Otherwise, we need GENERAL_REGS. */ + return GENERAL_REGS; +} + +/* START is the zero-based incoming argument register index used (0 is 160, + i.e., the first incoming argument register) and COUNT is the number used. + + Mark the corresponding incoming registers as neither fixed nor call used. + For each register used for incoming arguments, we have one less local + register that can be used. So also mark some high-numbered registers as + fixed. + + Return the first register number to use for the argument. */ + +int +incoming_reg (start, count) + int start; + int count; +{ + int i; + + if (! TARGET_NO_REUSE_ARGS) + /* Mark all the used registers as not fixed and saved over calls. */ + for (i = R_AR (start); i < R_AR (16) && i < R_AR (start + count); i++) + { + fixed_regs[i] = call_used_regs[i] = call_fixed_regs[i] = 0; + CLEAR_HARD_REG_BIT (fixed_reg_set, i); + CLEAR_HARD_REG_BIT (call_used_reg_set, i); + CLEAR_HARD_REG_BIT (call_fixed_reg_set, i); + } + + /* Shorten the maximum size of the frame. */ + for (i = R_AR (0) - start - count; i < R_AR (0) - start; i++) + { + fixed_regs[i] = call_used_regs[i] = call_fixed_regs[i] = 1; + SET_HARD_REG_BIT (fixed_reg_set, i); + SET_HARD_REG_BIT (call_used_reg_set, i); + SET_HARD_REG_BIT (call_fixed_reg_set, i); + } + + return R_AR (start); +} + +/* These routines are used in finding insns to fill delay slots in the + epilogue. */ + +/* Return 1 if the current function will adjust the register stack. */ + +int +needs_regstack_p () +{ + int i; + rtx insn; + + if (frame_pointer_needed) + return 1; + + /* If any local register is used, we need to adjust the regstack. */ + for (i = R_LR (127); i >= R_LR (0); i --) + if (regs_ever_live[i]) + return 1; + + /* We need a register stack if we make any calls. */ + for (insn = get_insns (); insn; insn = next_insn (insn)) + if (GET_CODE (insn) == CALL_INSN + || (GET_CODE (insn) == INSN + && GET_CODE (PATTERN (insn)) == SEQUENCE + && GET_CODE (XVECEXP (PATTERN (insn), 0, 0)) == CALL_INSN)) + return 1; + + /* Otherwise, we don't. */ + return 0; +} + +/* Return 1 if X uses a local register. */ + +int +uses_local_reg_p (x) + rtx x; +{ + char *fmt; + int i, j; + + switch (GET_CODE (x)) + { + case REG: + return REGNO (x) >= R_LR (0) && REGNO (x) <= R_FP; + + case CONST_INT: + case CONST: + case PC: + case CC0: + case LABEL_REF: + case SYMBOL_REF: + return 0; + } + + fmt = GET_RTX_FORMAT (GET_CODE (x)); + for (i = GET_RTX_LENGTH (GET_CODE (x)) - 1; i >= 0; i--) + { + if (fmt[i] == 'e') + { + if (uses_local_reg_p (XEXP (x, i))) + return 1; + } + else if (fmt[i] == 'E') + { + for (j = XVECLEN (x, i) - 1; j >= 0; j--) + if (uses_local_reg_p (XVECEXP (x, i, j))) + return 1; + } + } + + return 0; +} + +/* Returns 1 if this function is known to have a null epilogue. */ + +int +null_epilogue () +{ + return (reload_completed && ! needs_regstack_p () + && get_frame_size () == 0 + && current_function_pretend_args_size == 0); +} + +/* Write out the assembler form of an operand. Recognize the following + special options: + + %N means write the low-order 8 bits of the negative of the constant + %Q means write a QImode operand (truncate constants to 8 bits) + %M means write the low-order 16 bits of the constant + %C means write the low-order 8 bits of the complement of the constant + %X means write the cntl values for LOAD with operand an extension op + %b means write `f' is this is a reversed condition, `t' otherwise + %B means write `t' is this is a reversed condition, `f' otherwise + %J means write the 29k opcode part for a comparison operation + %e means write the label with an extra `X' is this is the epilogue + otherwise the normal label name + %E means write nothing if this insn has a delay slot, + a nop unless this is the epilogue label, in which case + write the first epilogue insn + %F means write just the normal operand if the insn has a delay slot; + otherwise, this is a recursive call so output the + symbol + 4 and write the first prologue insn in the + delay slot. + %L means write the register number plus one ("low order" register) + or the low-order part of a multi-word constant + %O means write the register number plus two + %P means write the register number plus three ("low order" of TImode) + %S means write the number of words in the mode of the operand, + minus one (for CR) + %V means write the number of elements in a PARALLEL minus 1 + %# means write nothing if we have a delay slot, "\n\tnop" otherwise + %* means write the register name for TPC. */ + +void +print_operand (file, x, code) + FILE *file; + rtx x; + char code; +{ + char buf[100]; + + /* These macros test for integers and extract the low-order bits. */ +#define INT_P(X) \ +((GET_CODE (X) == CONST_INT || GET_CODE (X) == CONST_DOUBLE) \ + && GET_MODE (X) == VOIDmode) + +#define INT_LOWPART(X) \ + (GET_CODE (X) == CONST_INT ? INTVAL (X) : CONST_DOUBLE_LOW (X)) + + switch (code) + { + case 'Q': + if (GET_CODE (x) == REG) + break; + else if (! INT_P (x)) + output_operand_lossage ("invalid %%Q value"); + fprintf (file, "%d", INT_LOWPART (x) & 0xff); + return; + + case 'C': + if (! INT_P (x)) + output_operand_lossage ("invalid %%C value"); + fprintf (file, "%d", (~ INT_LOWPART (x)) & 0xff); + return; + + case 'N': + if (! INT_P (x)) + output_operand_lossage ("invalid %%N value"); + fprintf (file, "%d", (- INT_LOWPART (x)) & 0xff); + return; + + case 'M': + if (! INT_P (x)) + output_operand_lossage ("invalid %%M value"); + fprintf (file, "%d", INT_LOWPART (x) & 0xffff); + return; + + case 'X': + fprintf (file, "%d", ((GET_MODE (XEXP (x, 0)) == QImode ? 1 : 2) + + (GET_CODE (x) == SIGN_EXTEND ? 16 : 0))); + return; + + case 'b': + if (GET_CODE (x) == GE) + fprintf (file, "f"); + else + fprintf (file, "t"); + return; + + case 'B': + if (GET_CODE (x) == GE) + fprintf (file, "t"); + else + fprintf (file, "f"); + return; + + case 'J': + /* It so happens that the RTX names for the conditions are the same as + the 29k's insns except for "ne", which requires "neq". */ + fprintf (file, GET_RTX_NAME (GET_CODE (x))); + if (GET_CODE (x) == NE) + fprintf (file, "q"); + return; + + case 'e': + if (optimize && flag_delayed_branch + && a29k_last_prologue_insn == 0 && epilogue_operand (x, VOIDmode) + && dbr_sequence_length () == 0) + { + /* We need to output the label number of the last label in the + function, which is not necessarily X since there might be + a USE insn in between. First go forward to the last insn, then + back up to a label. */ + while (NEXT_INSN (x) != 0) + x = NEXT_INSN (x); + + while (GET_CODE (x) != CODE_LABEL) + x = PREV_INSN (x); + + ASM_GENERATE_INTERNAL_LABEL (buf, "LX", CODE_LABEL_NUMBER (x)); + assemble_name (file, buf); + } + else + output_asm_label (x); + return; + + case 'E': + if (dbr_sequence_length ()) + ; + else if (a29k_last_prologue_insn) + { + fprintf (file, "\n\t%s", a29k_last_prologue_insn); + a29k_last_prologue_insn = 0; + } + else if (optimize && flag_delayed_branch + && epilogue_operand (x, VOIDmode)) + { + fprintf (file, "\n\t%s", a29k_first_epilogue_insn); + a29k_first_epilogue_insn_used = 1; + } + else + fprintf (file, "\n\tnop"); + return; + + case 'F': + output_addr_const (file, x); + if (! strcmp (XSTR (x, 0), current_function_name) + && dbr_sequence_length () == 0) + fprintf (file, "+4\n\t%s,%d", + a29k_regstack_size >= 64 ? "const gr121" : "sub gr1,gr1", + a29k_regstack_size * 4); + return; + + case 'L': + if (GET_CODE (x) == CONST_DOUBLE && GET_MODE (x) == DFmode) + { + union real_extract u; + + bcopy (&CONST_DOUBLE_LOW (x), &u, sizeof u); + fprintf (file, "$double1(%.20e)", u.d); + } + else if (GET_CODE (x) == REG) + fprintf (file, "%s", reg_names[REGNO (x) + 1]); + else + output_operand_lossage ("invalid %%L value"); + return; + + case 'O': + if (GET_CODE (x) != REG) + output_operand_lossage ("invalid %%O value"); + fprintf (file, "%s", reg_names[REGNO (x) + 2]); + return; + + case 'P': + if (GET_CODE (x) != REG) + output_operand_lossage ("invalid %%P value"); + fprintf (file, "%s", reg_names[REGNO (x) + 3]); + return; + + case 'S': + fprintf (file, "%d", (GET_MODE_SIZE (GET_MODE (x)) / UNITS_PER_WORD)-1); + return; + + case 'V': + if (GET_CODE (x) != PARALLEL) + output_operand_lossage ("invalid %%V value"); + fprintf (file, "%d", XVECLEN (x, 0) - 2); + return; + + case '#': + if (dbr_sequence_length () == 0) + { + if (a29k_last_prologue_insn) + { + fprintf (file, "\n\t%s", a29k_last_prologue_insn); + a29k_last_prologue_insn = 0; + } + else + fprintf (file, "\n\tnop"); + } + return; + + case '*': + fprintf (file, "%s", reg_names [R_TPC]); + return; + } + + if (GET_CODE (x) == REG) + fprintf (file, "%s", reg_names [REGNO (x)]); + + else if (GET_CODE (x) == MEM) + output_address (XEXP (x, 0)); + + else if (GET_CODE (x) == CONST && GET_CODE (XEXP (x, 0)) == SUBREG + && GET_CODE (SUBREG_REG (XEXP (x, 0))) == CONST_DOUBLE) + { + union real_extract u; + + if (GET_MODE (SUBREG_REG (XEXP (x, 0))) == SFmode) + fprintf (file, "$float"); + else + fprintf (file, "$double%d", SUBREG_WORD (XEXP (x, 0))); + bcopy (&CONST_DOUBLE_LOW (SUBREG_REG (XEXP (x, 0))), &u, sizeof u); + fprintf (file, "(%.20e)", u.d); + } + + else if (GET_CODE (x) == CONST_DOUBLE + && GET_MODE_CLASS (GET_MODE (x)) == MODE_FLOAT) + { + union real_extract u; + + bcopy (&CONST_DOUBLE_LOW (x), &u, sizeof u); + fprintf (file, "$%s(%.20e)", + GET_MODE (x) == SFmode ? "float" : "double0", u.d); + } + + else + output_addr_const (file, x); +} + +/* This page contains routines to output function prolog and epilog code. */ + +/* Output function prolog code to file FILE. Memory stack size is SIZE. + + Also sets register names for incoming arguments and frame pointer. */ + +void +output_prolog (file, size) + FILE *file; + int size; +{ + int makes_calls = 0; + int arg_count = 0; + rtx insn; + int i; + unsigned int tag_word; + + /* See if we make any calls. We need to set lr1 if so. */ + for (insn = get_insns (); insn; insn = next_insn (insn)) + if (GET_CODE (insn) == CALL_INSN + || (GET_CODE (insn) == INSN + && GET_CODE (PATTERN (insn)) == SEQUENCE + && GET_CODE (XVECEXP (PATTERN (insn), 0, 0)) == CALL_INSN)) + { + makes_calls = 1; + break; + } + + /* Find the highest local register used. */ + for (i = R_LR (127); i >= R_LR (0); i--) + if (regs_ever_live[i]) + break; + + a29k_regstack_size = i - (R_LR (0) - 1); + + /* If calling routines, ensure we count lr0 & lr1. */ + if (makes_calls && a29k_regstack_size < 2) + a29k_regstack_size = 2; + + /* Count frame pointer and align to 8 byte boundary (even number of + registers). */ + a29k_regstack_size += frame_pointer_needed; + if (a29k_regstack_size & 1) a29k_regstack_size++; + + /* See how many incoming arguments we have in registers. */ + for (i = R_AR (0); i < R_AR (16); i++) + if (! fixed_regs[i]) + arg_count++; + + /* The argument count includes the caller's lr0 and lr1. */ + arg_count += 2; + + /* Set the names and numbers of the frame pointer and incoming argument + registers. */ + + for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) + a29k_debug_reg_map[i] = i; + + reg_names[FRAME_POINTER_REGNUM] = reg_names[R_LR (a29k_regstack_size - 1)]; + a29k_debug_reg_map[FRAME_POINTER_REGNUM] = R_LR (a29k_regstack_size - 1); + + for (i = 0; i < 16; i++) + { + reg_names[R_AR (i)] = reg_names[R_LR (a29k_regstack_size + i + 2)]; + a29k_debug_reg_map[R_AR (i)] = R_LR (a29k_regstack_size + i + 2); + } + + /* Compute memory stack size. Add in number of bytes that the we should + push and pretend the caller did and the size of outgoing arguments. + Then round to a doubleword boundary. */ + size += (current_function_pretend_args_size + + current_function_outgoing_args_size); + size = (size + 7) & ~7; + + /* Write header words. See if one or two word form. */ + tag_word = (frame_pointer_needed ? 0x400000 : 0) + (arg_count << 16); + + if (size / 8 > 0xff) + fprintf (file, "\t.word %d, 0x%0x\n", (size / 8) << 2, + 0x800000 + tag_word); + else + fprintf (file, "\t.word 0x%0x\n", tag_word + ((size / 8) << 3)); + + /* Define the function name. */ + assemble_name (file, a29k_function_name); + fprintf (file, ":\n"); + + /* Push the register stack by the proper amount. There are two possible + ways to do this. */ + if (a29k_regstack_size >= 256/4) + fprintf (file, "\tconst %s,%d\n\tsub gr1,gr1,%s\n", + reg_names[R_TAV], a29k_regstack_size * 4, reg_names[R_TAV]); + else if (a29k_regstack_size) + fprintf (file, "\tsub gr1,gr1,%d\n", a29k_regstack_size * 4); + + /* Test that the registers are available. */ + if (a29k_regstack_size) + fprintf (file, "\tasgeu V_%sSPILL,gr1,%s\n", + TARGET_KERNEL_REGISTERS ? "K" : "", reg_names[R_RAB]); + + /* Set up frame pointer, if one is needed. */ + if (frame_pointer_needed) + fprintf (file, "\tsll %s,%s,0\n", reg_names[FRAME_POINTER_REGNUM], + reg_names[R_MSP]); + + /* Make room for any frame space. There are three ways to do this. */ + if (size >= 256) + { + fprintf (file, "\tconst %s,%d\n", reg_names[R_TAV], size); + if (size >= 65536) + fprintf (file, "\tconsth %s,%d\n", reg_names[R_TAV], size); + if (TARGET_STACK_CHECK) + fprintf (file, "\tcall %s,__msp_check\n", reg_names[R_TPC]); + fprintf (file, "\tsub %s,%s,%s\n", + reg_names[R_MSP], reg_names[R_MSP], reg_names[R_TAV]); + } + else if (size) + { + if (TARGET_STACK_CHECK) + fprintf (file, "\tcall %s,__msp_check\n", reg_names[R_TPC]); + fprintf (file, "\tsub %s,%s,%d\n", + reg_names[R_MSP], reg_names[R_MSP], size); + } + + /* If this routine will make calls, set lr1. If we see an insn that + can use a delay slot before a call or jump, save this insn for that + slot (this condition is equivalent to seeing if we have an insn that + needs delay slots before an insn that has a filled delay slot). */ + a29k_last_prologue_insn = 0; + if (makes_calls) + { + i = (a29k_regstack_size + arg_count) * 4; + if (i >= 256) + fprintf (file, "\tconst %s,%d\n\tsub lr1,gr1,%s\n", + reg_names[R_TAV], i, reg_names[R_TAV]); + else + { + if (optimize && flag_delayed_branch) + for (insn = get_insns (); insn; insn = NEXT_INSN (insn)) + { + if (GET_CODE (insn) == CODE_LABEL + || (GET_CODE (insn) == INSN + && GET_CODE (PATTERN (insn)) == SEQUENCE)) + break; + + if (GET_CODE (insn) == NOTE + || (GET_CODE (insn) == INSN + && (GET_CODE (PATTERN (insn)) == USE + || GET_CODE (PATTERN (insn)) == CLOBBER))) + continue; + + if (num_delay_slots (insn) > 0) + { + a29k_last_prologue_insn = (char *) oballoc (100); + sprintf (a29k_last_prologue_insn, "add lr1,gr1,%d", i); + break; + } + } + + if (a29k_last_prologue_insn == 0) + fprintf (file, "\tadd lr1,gr1,%d\n", i); + } + } + + /* Compute the first insn of the epilogue. */ + a29k_first_epilogue_insn_used = 0; + + if (size == 0 && a29k_regstack_size == 0 && ! frame_pointer_needed) + a29k_first_epilogue_insn = 0; + else + a29k_first_epilogue_insn = (char *) oballoc (100); + + if (frame_pointer_needed) + sprintf (a29k_first_epilogue_insn, "sll %s,%s,0", + reg_names[R_MSP], reg_names[FRAME_POINTER_REGNUM]); + else if (a29k_regstack_size) + { + if (a29k_regstack_size >= 256 / 4) + sprintf (a29k_first_epilogue_insn, "const %s,%d", + reg_names[R_TAV], a29k_regstack_size * 4); + else + sprintf (a29k_first_epilogue_insn, "add gr1,gr1,%d", + a29k_regstack_size * 4); + } + else if (size) + { + if (size >= 256) + sprintf (a29k_first_epilogue_insn, "const %s,%d", + reg_names[R_TAV], size); + else + sprintf (a29k_first_epilogue_insn, "add %s,%s,%d", + reg_names[R_MSP], reg_names[R_MSP], size); + } +} + +/* Call this after writing what might be the first instruction of the + epilogue. If that first insn was used in a delay slot, an intermediate + label is written. */ + +static void +check_epilogue_internal_label (file) + FILE *file; +{ + rtx insn; + + if (! a29k_first_epilogue_insn_used) + return; + + for (insn = get_last_insn (); + GET_CODE (insn) != CODE_LABEL; + insn = PREV_INSN (insn)) + ; + + ASM_OUTPUT_INTERNAL_LABEL (file, "LX", CODE_LABEL_NUMBER (insn)); + a29k_first_epilogue_insn_used = 0; +} + +/* Output the epilog of the last procedure to file FILE. SIZE is the memory + stack size. The register stack size is in the variable + A29K_REGSTACK_SIZE. */ + +void +output_epilog (file, size) + FILE *file; + int size; +{ + rtx insn; + int locals_unavailable = 0; /* True until after first insn + after gr1 update. */ + + /* If we hit a BARRIER before a real insn or CODE_LABEL, we don't + need to do anything because we are never jumped to. */ + insn = get_last_insn (); + if (GET_CODE (insn) == NOTE) + insn = prev_nonnote_insn (insn); + + if (insn && GET_CODE (insn) == BARRIER) + return; + + /* If a frame pointer was needed we must restore the memory stack pointer + before adjusting the register stack. */ + if (frame_pointer_needed) + { + fprintf (file, "\tsll %s,%s,0\n", + reg_names[R_MSP], reg_names[FRAME_POINTER_REGNUM]); + check_epilogue_internal_label (file); + } + + /* Restore the register stack. There are two ways to do this. */ + if (a29k_regstack_size) + { + if (a29k_regstack_size >= 256/4) + { + fprintf (file, "\tconst %s,%d\n", + reg_names[R_TAV], a29k_regstack_size * 4); + check_epilogue_internal_label (file); + fprintf (file, "\tadd gr1,gr1,%s\n", reg_names[R_TAV]); + } + else + { + fprintf (file, "\tadd gr1,gr1,%d\n", a29k_regstack_size * 4); + check_epilogue_internal_label (file); + } + locals_unavailable = 1; + } + + /* Restore the memory stack pointer if there is no frame pointer. + Adjust the size to include any pretend arguments and pushed + arguments and round to doubleword boundary. */ + size += (current_function_pretend_args_size + + current_function_outgoing_args_size); + size = (size + 7) & ~7; + + if (size && ! frame_pointer_needed) + { + if (size >= 256) + { + fprintf (file, "\tconst %s,%d\n", reg_names[R_TAV], size); + check_epilogue_internal_label (file); + locals_unavailable = 0; + if (size >= 65536) + fprintf (file, "\tconsth %s,%d\n", reg_names[R_TAV], size); + fprintf (file, "\tadd %s,%s,%s\n", + reg_names[R_MSP], reg_names[R_MSP], reg_names[R_TAV]); + } + else + { + fprintf (file, "\tadd %s,%s,%d\n", + reg_names[R_MSP], reg_names[R_MSP], size); + check_epilogue_internal_label (file); + locals_unavailable = 0; + } + } + + if (locals_unavailable) + { + /* If we have an insn for this delay slot, write it. */ + if (current_function_epilogue_delay_list) + final_scan_insn (XEXP (current_function_epilogue_delay_list, 0), + file, 1, -2, 1); + else + fprintf (file, "\tnop\n"); + } + + fprintf (file, "\tjmpi lr0\n"); + if (a29k_regstack_size) + fprintf (file, "\tasleu V_%sFILL,lr1,%s\n", + TARGET_KERNEL_REGISTERS ? "K" : "", reg_names[R_RFB]); + else if (current_function_epilogue_delay_list) + final_scan_insn (XEXP (current_function_epilogue_delay_list, 0), + file, 1, -2, 1); + else + fprintf (file, "\tnop\n"); +}