/* Subroutines for assembler code output on the NS32000.
- Copyright (C) 1988 Free Software Foundation, Inc.
+ Copyright (C) 1988, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001
+ Free Software Foundation, Inc.
This file is part of GNU CC.
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. */
+the Free Software Foundation, 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA. */
-/* Some output-actions in ns32k.md need these. */
-#include <stdio.h>
#include "config.h"
+#include "system.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 "function.h"
+#include "expr.h"
+#include "flags.h"
+#include "recog.h"
+#include "tm_p.h"
+#include "target.h"
+#include "target-def.h"
+#include "toplev.h"
#ifdef OSF_OS
int ns32k_num_files = 0;
#endif
-void
-trace (s, s1, s2)
- char *s, *s1, *s2;
+/* This duplicates reg_class_contents in reg_class.c, but maybe that isn't
+ initialized in time. Also this is more convenient as an array of ints.
+ We know that HARD_REG_SET fits in an unsigned int */
+
+unsigned int ns32k_reg_class_contents[N_REG_CLASSES][1] = REG_CLASS_CONTENTS;
+
+enum reg_class regclass_map[FIRST_PSEUDO_REGISTER] =
{
- fprintf (stderr, s, s1, s2);
-}
+ GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS,
+ GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS,
+ FLOAT_REG0, LONG_FLOAT_REG0, FLOAT_REGS, FLOAT_REGS,
+ FLOAT_REGS, FLOAT_REGS, FLOAT_REGS, FLOAT_REGS,
+ FP_REGS, FP_REGS, FP_REGS, FP_REGS,
+ FP_REGS, FP_REGS, FP_REGS, FP_REGS,
+ FRAME_POINTER_REG, STACK_POINTER_REG
+};
-/* Value is 1 if hard register REGNO can hold a value of machine-mode MODE. */
+static const char *const ns32k_out_reg_names[] = OUTPUT_REGISTER_NAMES;
-int
-hard_regno_mode_ok (regno, mode)
- int regno;
- enum machine_mode mode;
+static rtx gen_indexed_expr PARAMS ((rtx, rtx, rtx));
+static const char *singlemove_string PARAMS ((rtx *));
+static void move_tail PARAMS ((rtx[], int, int));
+static tree ns32k_handle_fntype_attribute PARAMS ((tree *, tree, tree, int, bool *));
+const struct attribute_spec ns32k_attribute_table[];
+static void ns32k_output_function_prologue PARAMS ((FILE *, HOST_WIDE_INT));
+static void ns32k_output_function_epilogue PARAMS ((FILE *, HOST_WIDE_INT));
+\f
+/* Initialize the GCC target structure. */
+#undef TARGET_ATTRIBUTE_TABLE
+#define TARGET_ATTRIBUTE_TABLE ns32k_attribute_table
+
+#undef TARGET_ASM_ALIGNED_HI_OP
+#define TARGET_ASM_ALIGNED_HI_OP "\t.word\t"
+
+#ifdef ENCORE_ASM
+#undef TARGET_ASM_ALIGNED_SI_OP
+#define TARGET_ASM_ALIGNED_SI_OP "\t.double\t"
+#endif
+
+#undef TARGET_ASM_FUNCTION_PROLOGUE
+#define TARGET_ASM_FUNCTION_PROLOGUE ns32k_output_function_prologue
+#undef TARGET_ASM_FUNCTION_EPILOGUE
+#define TARGET_ASM_FUNCTION_EPILOGUE ns32k_output_function_epilogue
+
+struct gcc_target targetm = TARGET_INITIALIZER;
+\f
+/* Generate the assembly code for function entry. FILE is a stdio
+ stream to output the code to. SIZE is an int: how many units of
+ temporary storage to allocate.
+
+ Refer to the array `regs_ever_live' to determine which registers to
+ save; `regs_ever_live[I]' is nonzero if register number I is ever
+ used in the function. This function is responsible for knowing
+ which registers should not be saved even if used. */
+
+/*
+ * The function prologue for the ns32k is fairly simple.
+ * If a frame pointer is needed (decided in reload.c ?) then
+ * we need assembler of the form
+ *
+ * # Save the oldframe pointer, set the new frame pointer, make space
+ * # on the stack and save any general purpose registers necessary
+ *
+ * enter [<general purpose regs to save>], <local stack space>
+ *
+ * movf fn, tos # Save any floating point registers necessary
+ * .
+ * .
+ *
+ * If a frame pointer is not needed we need assembler of the form
+ *
+ * # Make space on the stack
+ *
+ * adjspd <local stack space + 4>
+ *
+ * # Save any general purpose registers necessary
+ *
+ * save [<general purpose regs to save>]
+ *
+ * movf fn, tos # Save any floating point registers necessary
+ * .
+ * .
+ */
+
+#if !defined (MERLIN_TARGET) && !defined (UTEK_ASM)
+
+#if defined(IMMEDIATE_PREFIX) && IMMEDIATE_PREFIX
+#define ADJSP(FILE, N) \
+ fprintf (FILE, "\tadjspd %c%d\n", IMMEDIATE_PREFIX, (N))
+#else
+#define ADJSP(FILE, N) \
+ fprintf (FILE, "\tadjspd %d\n", (N))
+#endif
+
+static void
+ns32k_output_function_prologue (file, size)
+ FILE *file;
+ HOST_WIDE_INT size;
{
- switch (mode)
- {
- case QImode:
- case HImode:
- case PSImode:
- case SImode:
- case PDImode:
- case VOIDmode:
- case BLKmode:
- if (regno < 8 || regno == 16 || regno == 17)
- return 1;
+ register int regno, g_regs_used = 0;
+ int used_regs_buf[8], *bufp = used_regs_buf;
+ int used_fregs_buf[17], *fbufp = used_fregs_buf;
+
+ for (regno = R0_REGNUM; regno < F0_REGNUM; regno++)
+ if (regs_ever_live[regno]
+ && ! call_used_regs[regno])
+ {
+ *bufp++ = regno; g_regs_used++;
+ }
+ *bufp = -1;
+
+ for (; regno < FRAME_POINTER_REGNUM; regno++)
+ if (regs_ever_live[regno] && !call_used_regs[regno])
+ {
+ *fbufp++ = regno;
+ }
+ *fbufp = -1;
+
+ bufp = used_regs_buf;
+ if (frame_pointer_needed)
+ fprintf (file, "\tenter [");
+ else
+ {
+ if (size)
+ ADJSP (file, size + 4);
+ if (g_regs_used && g_regs_used > 4)
+ fprintf (file, "\tsave [");
else
- return 0;
+ {
+ while (*bufp >= 0)
+ fprintf (file, "\tmovd r%d,tos\n", *bufp++);
+ g_regs_used = 0;
+ }
+ }
- case DImode:
- if (regno < 8 && (regno & 1) == 0)
- return 1;
+ while (*bufp >= 0)
+ {
+ fprintf (file, "r%d", *bufp++);
+ if (*bufp >= 0)
+ fputc (',', file);
+ }
+
+ if (frame_pointer_needed)
+ fprintf (file, "],%d\n", size);
+ else if (g_regs_used)
+ fprintf (file, "]\n");
+
+ fbufp = used_fregs_buf;
+ while (*fbufp >= 0)
+ {
+ if ((*fbufp & 1) || (fbufp[0] != fbufp[1] - 1))
+ fprintf (file, "\tmovf %s,tos\n", ns32k_out_reg_names[*fbufp++]);
else
- return 0;
+ {
+ fprintf (file, "\tmovl %s,tos\n",
+ ns32k_out_reg_names[fbufp[0]]);
+ fbufp += 2;
+ }
+ }
- case SFmode:
- case SCmode:
- if (TARGET_32081)
+ if (flag_pic && current_function_uses_pic_offset_table)
+ {
+ fprintf (file, "\tsprd sb,tos\n");
+ if (TARGET_REGPARM)
{
- if (regno < 16)
- return 1;
- else
- return 0;
+ fprintf (file, "\taddr __GLOBAL_OFFSET_TABLE_(pc),tos\n");
+ fprintf (file, "\tlprd sb,tos\n");
}
else
{
- if (regno < 8)
- return 1;
- else
- return 0;
+ fprintf (file, "\taddr __GLOBAL_OFFSET_TABLE_(pc),r0\n");
+ fprintf (file, "\tlprd sb,r0\n");
}
+ }
+}
- case DFmode:
- case DCmode:
- if ((regno & 1) == 0)
- {
- if (TARGET_32081)
- {
- if (regno < 16)
- return 1;
- else
- return 0;
- }
- else
- {
- if (regno < 8)
- return 1;
- else
- return 0;
- }
+#else /* MERLIN_TARGET || UTEK_ASM */
+
+/* This differs from the standard one above in printing a bitmask
+ rather than a register list in the enter or save instruction. */
+
+static void
+ns32k_output_function_prologue (file, size)
+ FILE *file;
+ HOST_WIDE_INT size;
+{
+ register int regno, g_regs_used = 0;
+ int used_regs_buf[8], *bufp = used_regs_buf;
+ int used_fregs_buf[8], *fbufp = used_fregs_buf;
+
+ for (regno = 0; regno < 8; regno++)
+ if (regs_ever_live[regno]
+ && ! call_used_regs[regno])
+ {
+ *bufp++ = regno; g_regs_used++;
+ }
+ *bufp = -1;
+
+ for (; regno < 16; regno++)
+ if (regs_ever_live[regno] && !call_used_regs[regno]) {
+ *fbufp++ = regno;
+ }
+ *fbufp = -1;
+
+ bufp = used_regs_buf;
+ if (frame_pointer_needed)
+ fprintf (file, "\tenter ");
+ else if (g_regs_used)
+ fprintf (file, "\tsave ");
+
+ if (frame_pointer_needed || g_regs_used)
+ {
+ char mask = 0;
+ while (*bufp >= 0)
+ mask |= 1 << *bufp++;
+ fprintf (file, "$0x%x", (int) mask & 0xff);
+ }
+
+ if (frame_pointer_needed)
+#ifdef UTEK_ASM
+ fprintf (file, ",$%d\n", size);
+#else
+ fprintf (file, ",%d\n", size);
+#endif
+ else if (g_regs_used)
+ fprintf (file, "\n");
+
+ fbufp = used_fregs_buf;
+ while (*fbufp >= 0)
+ {
+ if ((*fbufp & 1) || (fbufp[0] != fbufp[1] - 1))
+ fprintf (file, "\tmovf f%d,tos\n", *fbufp++ - 8);
+ else
+ {
+ fprintf (file, "\tmovl f%d,tos\n", fbufp[0] - 8);
+ fbufp += 2;
}
+ }
+}
+
+#endif /* MERLIN_TARGET || UTEK_ASM */
+
+/* This function generates the assembly code for function exit,
+ on machines that need it.
+
+ The function epilogue should not depend on the current stack pointer,
+ if EXIT_IGNORE_STACK is nonzero. That doesn't apply here.
+
+ If a frame pointer is needed (decided in reload.c ?) then
+ we need assembler of the form
+
+ movf tos, fn # Restore any saved floating point registers
+ .
+ .
+
+ # Restore any saved general purpose registers, restore the stack
+ # pointer from the frame pointer, restore the old frame pointer.
+ exit [<general purpose regs to save>]
+
+ If a frame pointer is not needed we need assembler of the form
+ # Restore any general purpose registers saved
+
+ movf tos, fn # Restore any saved floating point registers
+ .
+ .
+ .
+ restore [<general purpose regs to save>]
+
+ # reclaim space allocated on stack
+
+ adjspd <-(local stack space + 4)> */
+
+#if !defined (MERLIN_TARGET) && !defined (UTEK_ASM)
+
+static void
+ns32k_output_function_epilogue (file, size)
+ FILE *file;
+ HOST_WIDE_INT size;
+{
+ register int regno, g_regs_used = 0, f_regs_used = 0;
+ int used_regs_buf[8], *bufp = used_regs_buf;
+ int used_fregs_buf[17], *fbufp = used_fregs_buf;
+
+ if (flag_pic && current_function_uses_pic_offset_table)
+ fprintf (file, "\tlprd sb,tos\n");
+
+ *fbufp++ = -2;
+ for (regno = F0_REGNUM; regno < FRAME_POINTER_REGNUM; regno++)
+ if (regs_ever_live[regno] && !call_used_regs[regno])
+ {
+ *fbufp++ = regno; f_regs_used++;
+ }
+ fbufp--;
+
+ for (regno = 0; regno < F0_REGNUM; regno++)
+ if (regs_ever_live[regno]
+ && ! call_used_regs[regno])
+ {
+ *bufp++ = regno; g_regs_used++;
+ }
+
+ while (fbufp > used_fregs_buf)
+ {
+ if ((*fbufp & 1) && fbufp[0] == fbufp[-1] + 1)
+ {
+ fprintf (file, "\tmovl tos,%s\n",
+ ns32k_out_reg_names[fbufp[-1]]);
+ fbufp -= 2;
+ }
+ else fprintf (file, "\tmovf tos,%s\n", ns32k_out_reg_names[*fbufp--]);
+ }
+
+ if (frame_pointer_needed)
+ fprintf (file, "\texit [");
+ else
+ {
+ if (g_regs_used && g_regs_used > 4)
+ fprintf (file, "\trestore [");
else
- return 0;
+ {
+ while (bufp > used_regs_buf)
+ fprintf (file, "\tmovd tos,r%d\n", *--bufp);
+ g_regs_used = 0;
+ }
+ }
+
+ while (bufp > used_regs_buf)
+ {
+ fprintf (file, "r%d", *--bufp);
+ if (bufp > used_regs_buf)
+ fputc (',', file);
+ }
+
+ if (g_regs_used || frame_pointer_needed)
+ fprintf (file, "]\n");
+
+ if (size && !frame_pointer_needed)
+ ADJSP (file, -(size + 4));
+
+ if (current_function_pops_args)
+ fprintf (file, "\tret %d\n", current_function_pops_args);
+ else
+ fprintf (file, "\tret 0\n");
+}
+
+#else /* MERLIN_TARGET || UTEK_ASM */
+
+/* This differs from the standard one above in printing a bitmask
+ rather than a register list in the exit or restore instruction. */
+
+static void
+ns32k_output_function_epilogue (file, size)
+ FILE *file;
+ HOST_WIDE_INT size ATTRIBUTE_UNUSED;
+{
+ register int regno, g_regs_used = 0, f_regs_used = 0;
+ int used_regs_buf[8], *bufp = used_regs_buf;
+ int used_fregs_buf[8], *fbufp = used_fregs_buf;
+
+ *fbufp++ = -2;
+ for (regno = 8; regno < 16; regno++)
+ if (regs_ever_live[regno] && !call_used_regs[regno]) {
+ *fbufp++ = regno; f_regs_used++;
}
+ fbufp--;
- /* Used to abort here, but simply saying "no" handles TImode
- much better. */
+ for (regno = 0; regno < 8; regno++)
+ if (regs_ever_live[regno]
+ && ! call_used_regs[regno])
+ {
+ *bufp++ = regno; g_regs_used++;
+ }
+
+ while (fbufp > used_fregs_buf)
+ {
+ if ((*fbufp & 1) && fbufp[0] == fbufp[-1] + 1)
+ {
+ fprintf (file, "\tmovl tos,f%d\n", fbufp[-1] - 8);
+ fbufp -= 2;
+ }
+ else fprintf (file, "\tmovf tos,f%d\n", *fbufp-- - 8);
+ }
+
+ if (frame_pointer_needed)
+ fprintf (file, "\texit ");
+ else if (g_regs_used)
+ fprintf (file, "\trestore ");
+
+ if (g_regs_used || frame_pointer_needed)
+ {
+ char mask = 0;
+
+ while (bufp > used_regs_buf)
+ {
+ /* Utek assembler takes care of reversing this */
+ mask |= 1 << *--bufp;
+ }
+ fprintf (file, "$0x%x\n", (int) mask & 0xff);
+ }
+
+#ifdef UTEK_ASM
+ if (current_function_pops_args)
+ fprintf (file, "\tret $%d\n", current_function_pops_args);
+ else
+ fprintf (file, "\tret $0\n");
+#else
+ if (current_function_pops_args)
+ fprintf (file, "\tret %d\n", current_function_pops_args);
+ else
+ fprintf (file, "\tret 0\n");
+#endif
+}
+
+#endif /* MERLIN_TARGET || UTEK_ASM */
+
+/* Value is 1 if hard register REGNO can hold a value of machine-mode MODE. */
+int
+hard_regno_mode_ok (regno, mode)
+ int regno;
+ enum machine_mode mode;
+{
+ int size = GET_MODE_UNIT_SIZE (mode);
+
+ if (FLOAT_MODE_P (mode))
+ {
+ if (size == UNITS_PER_WORD && regno < L1_REGNUM)
+ return 1;
+ if (size == UNITS_PER_WORD * 2
+ && (((regno & 1) == 0 && regno < FRAME_POINTER_REGNUM)))
+ return 1;
+ return 0;
+ }
+ if (size == UNITS_PER_WORD * 2
+ && (regno & 1) == 0 && regno < F0_REGNUM)
+ return 1;
+ if (size <= UNITS_PER_WORD
+ && (regno < F0_REGNUM || regno == FRAME_POINTER_REGNUM
+ || regno == STACK_POINTER_REGNUM))
+ return 1;
return 0;
}
+int register_move_cost (CLASS1, CLASS2)
+ enum reg_class CLASS1;
+ enum reg_class CLASS2;
+{
+ if (CLASS1 == NO_REGS || CLASS2 == NO_REGS)
+ return 2;
+ if ((SUBSET_P (CLASS1, FP_REGS) && !SUBSET_P (CLASS2, FP_REGS))
+ || (!SUBSET_P (CLASS1, FP_REGS) && SUBSET_P (CLASS2, FP_REGS)))
+ return 8;
+ if (((CLASS1) == STACK_POINTER_REG && !SUBSET_P (CLASS2,GENERAL_REGS))
+ || ((CLASS2) == STACK_POINTER_REG && !SUBSET_P (CLASS1,GENERAL_REGS)))
+ return 6;
+ if (((CLASS1) == FRAME_POINTER_REG && !SUBSET_P (CLASS2,GENERAL_REGS))
+ || ((CLASS2) == FRAME_POINTER_REG && !SUBSET_P (CLASS1,GENERAL_REGS)))
+ return 6;
+ return 2;
+}
+
+#if 0
+/* We made the insn definitions copy from floating point to general
+ registers via the stack. */
+int secondary_memory_needed (CLASS1, CLASS2, M)
+ enum reg_class CLASS1;
+ enum reg_class CLASS2;
+ enum machine_mode M;
+{
+ int ret = ((SUBSET_P (CLASS1, FP_REGS) && !SUBSET_P (CLASS2, FP_REGS))
+ || (!SUBSET_P (CLASS1, FP_REGS) && SUBSET_P (CLASS2, FP_REGS)));
+ return ret;
+}
+#endif
+
+
/* ADDRESS_COST calls this. This function is not optimal
for the 32032 & 32332, but it probably is better than
the default. */
{
int i;
int cost = 0;
-
if (GET_CODE (operand) == MEM)
cost += 3;
if (GET_CODE (operand) == MULT)
cost += 2;
-#if 0
- if (GET_CODE (operand) == REG)
- cost += 1; /* not really, but the documentation
- says different amount of registers
- shouldn't return the same costs */
-#endif
switch (GET_CODE (operand))
{
case REG:
- case CONST:
- case CONST_INT:
- case CONST_DOUBLE:
- case SYMBOL_REF:
- case LABEL_REF:
+ cost += 1;
+ break;
case POST_DEC:
case PRE_DEC:
break;
- case MULT:
+ case CONST_INT:
+ if (INTVAL (operand) <= 7 && INTVAL (operand) >= -8)
+ break;
+ if (INTVAL (operand) < 0x2000 && INTVAL (operand) >= -0x2000)
+ {
+ cost +=1;
+ break;
+ }
+ case CONST:
+ case LABEL_REF:
+ case SYMBOL_REF:
+ cost +=3;
+ break;
+ case CONST_DOUBLE:
+ cost += 5;
+ break;
case MEM:
+ cost += calc_address_cost (XEXP (operand, 0));
+ break;
+ case MULT:
case PLUS:
for (i = 0; i < GET_RTX_LENGTH (GET_CODE (operand)); i++)
{
enum reg_class
secondary_reload_class (class, mode, in)
enum reg_class class;
- enum machine_mode mode;
+ enum machine_mode mode ATTRIBUTE_UNUSED;
rtx in;
{
int regno = true_regnum (in);
if (regno >= FIRST_PSEUDO_REGISTER)
regno = -1;
- /* We can place anything into GENERAL_REGS and can put GENERAL_REGS
- into anything. */
- if (class == GENERAL_REGS || (regno >= 0 && regno < 8))
- return NO_REGS;
-
- /* Constants, memory, and FP registers can go into FP registers. */
- if ((regno == -1 || (regno >= 8 && regno < 16)) && (class == FLOAT_REGS))
- return NO_REGS;
-
-#if 0 /* This isn't strictly true (can't move fp to sp or vice versa),
- so it's cleaner to use PREFERRED_RELOAD_CLASS
- to make the right things happen. */
- if (regno >= 16 && class == GEN_AND_MEM_REGS)
+ if ((class == FRAME_POINTER_REG && regno == STACK_POINTER_REGNUM)
+ || ( class == STACK_POINTER_REG && regno == FRAME_POINTER_REGNUM))
+ return GENERAL_REGS;
+ else
return NO_REGS;
-#endif
-
- /* Otherwise, we need GENERAL_REGS. */
- return GENERAL_REGS;
}
+
/* Generate the rtx that comes from an address expression in the md file */
/* The expression to be build is BASE[INDEX:SCALE]. To recognize this,
scale must be converted from an exponent (from ASHIFT) to a
multiplier (for MULT). */
-rtx
+
+static rtx
gen_indexed_expr (base, index, scale)
rtx base, index, scale;
{
rtx addr;
- /* This generates an illegal addressing mode, if BASE is
+ /* This generates an invalid addressing mode, if BASE is
fp or sp. This is handled by PRINT_OPERAND_ADDRESS. */
if (GET_CODE (base) != REG && GET_CODE (base) != CONST_INT)
- base = gen_rtx (MEM, SImode, base);
- addr = gen_rtx (MULT, SImode, index,
- gen_rtx (CONST_INT, VOIDmode, 1 << INTVAL (scale)));
- addr = gen_rtx (PLUS, SImode, base, addr);
+ base = gen_rtx_MEM (SImode, base);
+ addr = gen_rtx_MULT (SImode, index,
+ GEN_INT (1 << INTVAL (scale)));
+ addr = gen_rtx_PLUS (SImode, base, addr);
return addr;
}
-/* Return 1 if OP is a valid operand of mode MODE. This
- predicate rejects operands which do not have a mode
- (such as CONST_INT which are VOIDmode). */
-int
-reg_or_mem_operand (op, mode)
- register rtx op;
- enum machine_mode mode;
+\f
+/* 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[];
{
- return (GET_MODE (op) == mode
- && (GET_CODE (op) == REG
- || GET_CODE (op) == SUBREG
- || GET_CODE (op) == MEM));
+ 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] = adjust_address (operands[num], SImode, 4);
+ }
+ else
+ abort ();
+ }
}
\f
/* Return the best assembler insn template
for moving operands[1] into operands[0] as a fullword. */
-static char *
+static const char *
singlemove_string (operands)
rtx *operands;
{
return "movd %1,%0";
}
-char *
+const char *
output_move_double (operands)
rtx *operands;
{
- enum anon1 { REGOP, OFFSOP, POPOP, CNSTOP, RNDOP } optype0, optype1;
+ enum anon1 { REGOP, OFFSOP, PUSHOP, CNSTOP, RNDOP } optype0, optype1;
rtx latehalf[2];
/* First classify both operands. */
else if (offsettable_memref_p (operands[0]))
optype0 = OFFSOP;
else if (GET_CODE (XEXP (operands[0], 0)) == PRE_DEC)
- optype0 = POPOP;
+ optype0 = PUSHOP;
else
optype0 = RNDOP;
if (REG_P (operands[1]))
optype1 = REGOP;
- else if (CONSTANT_ADDRESS_P (operands[1])
+ else if (CONSTANT_P (operands[1])
|| GET_CODE (operands[1]) == CONST_DOUBLE)
optype1 = CNSTOP;
else if (offsettable_memref_p (operands[1]))
optype1 = OFFSOP;
else if (GET_CODE (XEXP (operands[1], 0)) == PRE_DEC)
- optype1 = POPOP;
+ optype1 = PUSHOP;
else
optype1 = RNDOP;
operands in OPERANDS to be suitable for the low-numbered word. */
if (optype0 == REGOP)
- latehalf[0] = gen_rtx (REG, SImode, REGNO (operands[0]) + 1);
+ latehalf[0] = gen_rtx_REG (SImode, REGNO (operands[0]) + 1);
else if (optype0 == OFFSOP)
- latehalf[0] = adj_offsettable_operand (operands[0], 4);
+ latehalf[0] = adjust_address (operands[0], SImode, 4);
else
latehalf[0] = operands[0];
if (optype1 == REGOP)
- latehalf[1] = gen_rtx (REG, SImode, REGNO (operands[1]) + 1);
+ latehalf[1] = gen_rtx_REG (SImode, REGNO (operands[1]) + 1);
else if (optype1 == OFFSOP)
- latehalf[1] = adj_offsettable_operand (operands[1], 4);
+ latehalf[1] = adjust_address (operands[1], SImode, 4);
else if (optype1 == CNSTOP)
- {
- if (CONSTANT_ADDRESS_P (operands[1]))
- latehalf[1] = const0_rtx;
- else if (GET_CODE (operands[1]) == CONST_DOUBLE)
- split_double (operands[1], &operands[1], &latehalf[1]);
- }
+ split_double (operands[1], &operands[1], &latehalf[1]);
else
latehalf[1] = operands[1];
+ /* If insn is effectively movd N(sp),tos 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.
+ Given this, it doesn't matter which half we do "first". */
+ 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. */
-
- if (optype0 == POPOP || optype1 == POPOP)
+ else if (optype0 == PUSHOP || optype1 == PUSHOP)
{
output_asm_insn (singlemove_string (latehalf), latehalf);
return singlemove_string (operands);
&& reg_mentioned_p (latehalf[0], XEXP (operands[1], 0)))
{
/* If both halves of dest are used in the src memory address,
- add the two regs and put them in the low reg (operands[0]).
+ load the destination address into the low reg (operands[0]).
Then it works to load latehalf first. */
rtx xops[2];
- xops[0] = latehalf[0];
+ xops[0] = XEXP (operands[1], 0);
xops[1] = operands[0];
- output_asm_insn ("addd %0,%1", xops);
- operands[1] = gen_rtx (MEM, DImode, operands[0]);
- latehalf[1] = adj_offsettable_operand (operands[1], 4);
+ output_asm_insn ("addr %a0,%1", xops);
+ operands[1] = gen_rtx_MEM (DImode, operands[0]);
+ latehalf[1] = adjust_address (operands[1], SImode, 4);
/* The first half has the overlap, Do the late half first. */
output_asm_insn (singlemove_string (latehalf), latehalf);
/* Then clobber. */
return singlemove_string (operands);
}
+\f
+#define MAX_UNALIGNED_COPY (32)
+/* Expand string/block move operations.
+
+ operands[0] is the pointer to the destination.
+ operands[1] is the pointer to the source.
+ operands[2] is the number of bytes to move.
+ operands[3] is the alignment. */
+
+static void
+move_tail (operands, bytes, offset)
+ rtx operands[];
+ int bytes;
+ int offset;
+{
+ if (bytes & 2)
+ {
+ emit_move_insn (adjust_address (operands[0], HImode, offset),
+ adjust_address (operands[1], HImode, offset));
+ offset += 2;
+ }
+ if (bytes & 1)
+ emit_move_insn (adjust_address (operands[0], QImode, offset),
+ adjust_address (operands[1], QImode, offset));
+}
+
+void
+expand_block_move (operands)
+ rtx operands[];
+{
+ rtx bytes_rtx = operands[2];
+ rtx align_rtx = operands[3];
+ int constp = (GET_CODE (bytes_rtx) == CONST_INT);
+ int bytes = (constp ? INTVAL (bytes_rtx) : 0);
+ int align = INTVAL (align_rtx);
+ rtx src_reg = gen_rtx_REG (Pmode, 1);
+ rtx dest_reg = gen_rtx_REG (Pmode, 2);
+ rtx count_reg = gen_rtx_REG (SImode, 0);
+
+ if (constp && bytes <= 0)
+ return;
+
+ if (constp && bytes < 20)
+ {
+ int words = bytes >> 2;
+
+ if (words)
+ {
+ if (words < 3 || flag_unroll_loops)
+ {
+ int offset = 0;
+
+ for (; words; words--, offset += 4)
+ emit_move_insn (adjust_address (operands[0], SImode, offset),
+ adjust_address (operands[1], SImode, offset));
+ }
+ else
+ {
+ /* Use movmd. It is slower than multiple movd's but more
+ compact. It is also slower than movsd for large copies
+ but causes less registers reloading so is better than movsd
+ for small copies. */
+ rtx src, dest;
+ dest = copy_addr_to_reg (XEXP (operands[0], 0));
+ src = copy_addr_to_reg (XEXP (operands[1], 0));
+
+ emit_insn (gen_movstrsi2(dest, src, GEN_INT (words)));
+ }
+ }
+ move_tail (operands, bytes & 3, bytes & ~3);
+ return;
+ }
+
+ if (align > UNITS_PER_WORD)
+ align = UNITS_PER_WORD;
+
+ /* Move the address into scratch registers. */
+ emit_insn (gen_rtx_CLOBBER (VOIDmode, dest_reg));
+ emit_move_insn (dest_reg, XEXP (operands[0], 0));
+ operands[0] = gen_rtx_MEM (SImode, dest_reg);
+ emit_insn (gen_rtx_CLOBBER (VOIDmode, src_reg));
+ emit_move_insn (src_reg, XEXP (operands[1], 0));
+ operands[1] = gen_rtx_MEM (SImode, src_reg);
+ emit_insn (gen_rtx_CLOBBER (VOIDmode, count_reg));
+
+ if (constp && (align == UNITS_PER_WORD || bytes < MAX_UNALIGNED_COPY))
+ {
+ /* constant no of bytes and aligned or small enough copy to not bother
+ * aligning. Emit insns to copy by words.
+ */
+ if (bytes >> 2)
+ {
+ emit_move_insn (count_reg, GEN_INT (bytes >> 2));
+ emit_insn (gen_movstrsi1 (GEN_INT (4)));
+ }
+ /* insns to copy rest */
+ move_tail (operands, bytes & 3, 0);
+ }
+ else if (align == UNITS_PER_WORD)
+ {
+ /* insns to copy by words */
+ emit_insn (gen_lshrsi3 (count_reg, bytes_rtx, GEN_INT (2)));
+ emit_insn (gen_movstrsi1 (GEN_INT (4)));
+ if (constp)
+ {
+ move_tail (operands, bytes & 3, 0);
+ }
+ else
+ {
+ /* insns to copy rest */
+ emit_insn (gen_andsi3 (count_reg, bytes_rtx, GEN_INT (3)));
+ emit_insn (gen_movstrsi1 (const1_rtx));
+ }
+ }
+ else
+ {
+ /* Not aligned and we may have a lot to copy so it is worth
+ * aligning.
+ */
+ rtx aligned_label = gen_label_rtx ();
+ rtx bytes_reg;
+
+ bytes_reg = copy_to_mode_reg (SImode, bytes_rtx);
+ if (!constp)
+ {
+ /* Emit insns to test and skip over the alignment if it is
+ * not worth it. This doubles as a test to ensure that the alignment
+ * operation can't copy too many bytes
+ */
+ emit_insn (gen_cmpsi (bytes_reg, GEN_INT (MAX_UNALIGNED_COPY)));
+ emit_jump_insn (gen_blt (aligned_label));
+ }
+
+ /* Emit insns to do alignment at run time */
+ emit_insn (gen_negsi2 (count_reg, src_reg));
+ emit_insn (gen_andsi3 (count_reg, count_reg, GEN_INT (3)));
+ emit_insn (gen_subsi3 (bytes_reg, bytes_reg, count_reg));
+ emit_insn (gen_movstrsi1 (const1_rtx));
+ if (!constp)
+ emit_label (aligned_label);
+
+ /* insns to copy by words */
+ emit_insn (gen_lshrsi3 (count_reg, bytes_reg, GEN_INT (2)));
+ emit_insn (gen_movstrsi1 (GEN_INT (4)));
+
+ /* insns to copy rest */
+ emit_insn (gen_andsi3 (count_reg, bytes_reg, GEN_INT (3)));
+ emit_insn (gen_movstrsi1 (const1_rtx));
+ }
+}
+\f
+
+/* Returns 1 if OP contains a global symbol reference */
+
int
-check_reg (oper, reg)
- rtx oper;
- int reg;
+global_symbolic_reference_mentioned_p (op, f)
+ rtx op;
+ int f;
{
+ register const char *fmt;
register int i;
- if (oper == 0)
+ if (GET_CODE (op) == SYMBOL_REF)
+ {
+ if (! SYMBOL_REF_FLAG (op))
+ return 1;
+ else
+ return 0;
+ }
+ else if (f && GET_CODE (op) != CONST)
return 0;
- switch (GET_CODE(oper))
+
+ fmt = GET_RTX_FORMAT (GET_CODE (op));
+ for (i = GET_RTX_LENGTH (GET_CODE (op)) - 1; i >= 0; i--)
{
- case REG:
- return (REGNO(oper) == reg) ? 1 : 0;
- case MEM:
- return check_reg(XEXP(oper, 0), reg);
- case PLUS:
- case MULT:
- return check_reg(XEXP(oper, 0), reg) || check_reg(XEXP(oper, 1), reg);
+ if (fmt[i] == 'E')
+ {
+ register int j;
+
+ for (j = XVECLEN (op, i) - 1; j >= 0; j--)
+ if (global_symbolic_reference_mentioned_p (XVECEXP (op, i, j), 0))
+ return 1;
+ }
+ else if (fmt[i] == 'e'
+ && global_symbolic_reference_mentioned_p (XEXP (op, i), 0))
+ return 1;
+ }
+
+ return 0;
+}
+
+\f
+/* Returns 1 if OP contains a symbol reference */
+
+int
+symbolic_reference_mentioned_p (op)
+ rtx op;
+{
+ register const 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;
+}
+\f
+/* Table of machine-specific attributes. */
+
+const struct attribute_spec ns32k_attribute_table[] =
+{
+ /* { name, min_len, max_len, decl_req, type_req, fn_type_req, handler } */
+ /* Stdcall attribute says callee is responsible for popping arguments
+ if they are not variable. */
+ { "stdcall", 0, 0, false, true, true, ns32k_handle_fntype_attribute },
+ /* Cdecl attribute says the callee is a normal C declaration */
+ { "cdecl", 0, 0, false, true, true, ns32k_handle_fntype_attribute },
+ { NULL, 0, 0, false, false, false, NULL }
+};
+
+/* Handle an attribute requiring a FUNCTION_TYPE, FIELD_DECL or TYPE_DECL;
+ arguments as in struct attribute_spec.handler. */
+static tree
+ns32k_handle_fntype_attribute (node, name, args, flags, no_add_attrs)
+ tree *node;
+ tree name;
+ tree args ATTRIBUTE_UNUSED;
+ int flags ATTRIBUTE_UNUSED;
+ bool *no_add_attrs;
+{
+ if (TREE_CODE (*node) != FUNCTION_TYPE
+ && TREE_CODE (*node) != FIELD_DECL
+ && TREE_CODE (*node) != TYPE_DECL)
+ {
+ warning ("`%s' attribute only applies to functions",
+ IDENTIFIER_POINTER (name));
+ *no_add_attrs = true;
+ }
+
+ return NULL_TREE;
+}
+
+\f
+/* Value is the number of bytes of arguments automatically
+ popped when returning from a subroutine call.
+ FUNDECL is the declaration node of the function (as a tree),
+ FUNTYPE is the data type of the function (as a tree),
+ or for a library call it is an identifier node for the subroutine name.
+ SIZE is the number of bytes of arguments passed on the stack.
+
+ On the ns32k, the RET insn may be used to pop them if the number
+ of args is fixed, but if the number is variable then the caller
+ must pop them all. RET can't be used for library calls now
+ because the library is compiled with the Unix compiler.
+ Use of RET is a selectable option, since it is incompatible with
+ standard Unix calling sequences. If the option is not selected,
+ the caller must always pop the args.
+
+ The attribute stdcall is equivalent to RET on a per module basis. */
+
+int
+ns32k_return_pops_args (fundecl, funtype, size)
+ tree fundecl ATTRIBUTE_UNUSED;
+ tree funtype;
+ int size;
+{
+ int rtd = TARGET_RTD;
+
+ if (TREE_CODE (funtype) == IDENTIFIER_NODE)
+ return rtd ? size : 0;
+
+ /* Cdecl functions override -mrtd, and never pop the stack */
+ if (lookup_attribute ("cdecl", TYPE_ATTRIBUTES (funtype)))
+ return 0;
+
+ /* Stdcall functions will pop the stack if not variable args */
+ if (lookup_attribute ("stdcall", TYPE_ATTRIBUTES (funtype)))
+ rtd = 1;
+
+ if (rtd)
+ {
+ if (TYPE_ARG_TYPES (funtype) == NULL_TREE
+ || (TREE_VALUE (tree_last (TYPE_ARG_TYPES (funtype))) == void_type_node))
+ return size;
}
+
return 0;
}
\f
which is easier to debug than putting all the code in
a macro definition in ns32k.h. */
+/* XXX time 12% of cpu time is in fprintf for non optimizing */
void
print_operand (file, x, code)
FILE *file;
rtx x;
- char code;
+ int code;
{
if (code == '$')
PUT_IMMEDIATE_PREFIX (file);
else if (code == '?')
PUT_EXTERNAL_PREFIX (file);
else if (GET_CODE (x) == REG)
- fprintf (file, "%s", reg_names[REGNO (x)]);
+ fprintf (file, "%s", ns32k_out_reg_names[REGNO (x)]);
else if (GET_CODE (x) == MEM)
{
- rtx tmp = XEXP (x, 0);
-#if ! (defined (PC_RELATIVE) || defined (NO_ABSOLUTE_PREFIX_IF_SYMBOLIC))
- if (GET_CODE (tmp) != CONST_INT)
- {
- char *out = XSTR (tmp, 0);
- if (out[0] == '*')
- {
- PUT_ABSOLUTE_PREFIX (file);
- fprintf (file, "%s", &out[1]);
- }
- else
- ASM_OUTPUT_LABELREF (file, out);
- }
- else
-#endif
- output_address (XEXP (x, 0));
+ output_address (XEXP (x, 0));
}
- else if (GET_CODE (x) == CONST_DOUBLE && GET_MODE (x) != DImode)
+ else if (GET_CODE (x) == CONST_DOUBLE && GET_MODE (x) != VOIDmode)
{
if (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);
- PUT_IMMEDIATE_PREFIX(file);
+ PUT_IMMEDIATE_PREFIX (file);
#ifdef SEQUENT_ASM
- /* Sequent likes it's floating point constants as integers */
+ /* Sequent likes its floating point constants as integers */
fprintf (file, "0Dx%08x%08x", u.i[1], u.i[0]);
#else
#ifdef ENCORE_ASM
{
union { float f; long l; } uu;
uu.f = u.d;
- fprintf (file, "0Fx%08x", uu.l);
+ fprintf (file, "0Fx%08lx", uu.l);
}
#else
fprintf (file, "0f%.20e", u.d);
}
else
{
+ if (flag_pic
+ && GET_CODE (x) == CONST
+ && symbolic_reference_mentioned_p (x))
+ {
+ fprintf (stderr, "illegal constant for pic-mode: \n");
+ print_rtl (stderr, x);
+ fprintf (stderr, "\nGET_CODE (x) == %d, CONST == %d, symbolic_reference_mentioned_p (x) == %d\n",
+ GET_CODE (x), CONST, symbolic_reference_mentioned_p (x));
+ abort ();
+ }
+ else if (flag_pic
+ && (GET_CODE (x) == SYMBOL_REF || GET_CODE (x) == LABEL_REF))
+ {
+ output_addr_const (file, x);
+ fprintf (file, "(sb)");
+ }
+ else
+ {
#ifdef NO_IMMEDIATE_PREFIX_IF_SYMBOLIC
- if (GET_CODE (x) == CONST_INT)
+ if (GET_CODE (x) == CONST_INT)
#endif
- PUT_IMMEDIATE_PREFIX (file);
- output_addr_const (file, x);
+ PUT_IMMEDIATE_PREFIX (file);
+ output_addr_const (file, x);
+ }
}
}
\f
figure out how it worked.
90-11-25 Tatu Yl|nen <ylo@cs.hut.fi> */
+void
print_operand_address (file, addr)
register FILE *file;
register rtx addr;
{
- static char scales[] = { 'b', 'w', 'd', 0, 'q', };
+ static const char scales[] = { 'b', 'w', 'd', 0, 'q', };
rtx offset, base, indexexp, tmp;
int scale;
+ extern int flag_pic;
if (GET_CODE (addr) == PRE_DEC || GET_CODE (addr) == POST_DEC)
{
base = tmp;
break;
case REG:
- if (REGNO (tmp) < 8)
+ if (REGNO (tmp) < F0_REGNUM)
if (base)
{
indexexp = tmp;
case MULT:
indexexp = tmp;
break;
+ case SYMBOL_REF:
+ if (flag_pic && ! CONSTANT_POOL_ADDRESS_P (tmp)
+ && ! SYMBOL_REF_FLAG (tmp))
+ {
+ if (base)
+ {
+ if (indexexp)
+ abort ();
+ indexexp = base;
+ }
+ base = tmp;
+ break;
+ }
case CONST:
+ if (flag_pic && GET_CODE (tmp) == CONST)
+ {
+ rtx sym, off, tmp1;
+ tmp1 = XEXP (tmp,0);
+ if (GET_CODE (tmp1) != PLUS)
+ abort ();
+
+ sym = XEXP (tmp1,0);
+ if (GET_CODE (sym) != SYMBOL_REF)
+ {
+ off = sym;
+ sym = XEXP (tmp1,1);
+ }
+ else
+ off = XEXP (tmp1,1);
+ if (GET_CODE (sym) == SYMBOL_REF)
+ {
+ if (GET_CODE (off) != CONST_INT)
+ abort ();
+
+ if (CONSTANT_POOL_ADDRESS_P (sym)
+ || SYMBOL_REF_FLAG (sym))
+ {
+ SYMBOL_REF_FLAG (tmp) = 1;
+ }
+ else
+ {
+ if (base)
+ {
+ if (indexexp)
+ abort ();
+
+ indexexp = base;
+ }
+
+ if (offset != 0)
+ abort ();
+
+ base = sym;
+ offset = off;
+ break;
+ }
+ }
+ }
case CONST_INT:
- case SYMBOL_REF:
case LABEL_REF:
if (offset)
- offset = gen_rtx (PLUS, SImode, tmp, offset);
+ offset = gen_rtx_PLUS (SImode, tmp, offset);
else
offset = tmp;
break;
if (! offset)
offset = const0_rtx;
-#ifdef INDEX_RATHER_THAN_BASE
+ if (base
+#ifndef INDEX_RATHER_THAN_BASE
+ && (flag_pic || TARGET_HIMEM)
+ && GET_CODE (base) != SYMBOL_REF
+ && GET_CODE (offset) != CONST_INT
+#else
/* This is a re-implementation of the SEQUENT_ADDRESS_BUG fix. */
- if (base && !indexexp && GET_CODE (base) == REG
+#endif
+ && !indexexp && GET_CODE (base) == REG
&& REG_OK_FOR_INDEX_P (base))
{
indexexp = base;
- base = 0;
+ base = NULL;
}
-#endif
/* now, offset, base and indexexp are set */
+#ifndef BASE_REG_NEEDED
if (! base)
{
#if defined (PC_RELATIVE) || defined (NO_ABSOLUTE_PREFIX_IF_SYMBOLIC)
if (GET_CODE (offset) == CONST_INT)
-/* if (! (GET_CODE (offset) == LABEL_REF
- || GET_CODE (offset) == SYMBOL_REF)) */
#endif
PUT_ABSOLUTE_PREFIX (file);
}
+#endif
output_addr_const (file, offset);
if (base) /* base can be (REG ...) or (MEM ...) */
(disp(sb)) (MEM ...)
*/
case REG:
- fprintf (file, "(%s)", reg_names[REGNO (base)]);
+ fprintf (file, "(%s)", ns32k_out_reg_names[REGNO (base)]);
break;
+ case SYMBOL_REF:
+ if (! flag_pic)
+ abort ();
+
+ fprintf (file, "(");
+ output_addr_const (file, base);
+ fprintf (file, "(sb))");
+ break;
case MEM:
- addr = XEXP(base,0);
+ addr = XEXP (base,0);
base = NULL;
offset = NULL;
while (addr != NULL)
case SYMBOL_REF:
case LABEL_REF:
if (offset)
- offset = gen_rtx (PLUS, SImode, tmp, offset);
+ offset = gen_rtx_PLUS (SImode, tmp, offset);
else
offset = tmp;
break;
fprintf (file, "(");
output_addr_const (file, offset);
if (base)
- fprintf (file, "(%s)", reg_names[REGNO (base)]);
-#ifdef BASE_REG_NEEDED
+ fprintf (file, "(%s)", ns32k_out_reg_names[REGNO (base)]);
else if (TARGET_SB)
fprintf (file, "(sb)");
else
abort ();
-#endif
fprintf (file, ")");
break;
-
default:
abort ();
}
#ifdef PC_RELATIVE
- else /* no base */
- if (GET_CODE (offset) == LABEL_REF || GET_CODE (offset) == SYMBOL_REF)
- fprintf (file, "(pc)");
-#endif
-#ifdef BASE_REG_NEEDED /* this is defined if the assembler always
- needs a base register */
- else if (TARGET_SB)
- fprintf (file, "(sb)");
- else
- abort ();
+ else if (GET_CODE (offset) != CONST_INT)
+ fprintf (file, "(pc)");
+#ifdef BASE_REG_NEEDED
+ else if (TARGET_SB)
+ fprintf (file, "(sb)");
+ else
+ abort ();
#endif
+#endif /* PC_RELATIVE */
+
/* now print index if we have one */
if (indexexp)
{
}
else
scale = 0;
- if (GET_CODE (indexexp) != REG || REGNO (indexexp) >= 8)
+ if (GET_CODE (indexexp) != REG || REGNO (indexexp) >= F0_REGNUM)
abort ();
#ifdef UTEK_ASM
fprintf (file, "[%c`%s]",
scales[scale],
- reg_names[REGNO (indexexp)]);
+ ns32k_out_reg_names[REGNO (indexexp)]);
#else
fprintf (file, "[%s:%c]",
- reg_names[REGNO (indexexp)],
+ ns32k_out_reg_names[REGNO (indexexp)],
scales[scale]);
#endif
}
/* National 32032 shifting is so bad that we can get
better performance in many common cases by using other
techniques. */
-char *
+const char *
output_shift_insn (operands)
rtx *operands;
{
if (GET_CODE (operands[2]) == CONST_INT
&& INTVAL (operands[2]) > 0
&& INTVAL (operands[2]) <= 3)
- if (GET_CODE (operands[0]) == REG)
- {
- if (GET_CODE (operands[1]) == REG)
- {
- if (REGNO (operands[0]) == REGNO (operands[1]))
- {
- if (operands[2] == const1_rtx)
- return "addd %0,%0";
- else if (INTVAL (operands[2]) == 2)
- return "addd %0,%0\n\taddd %0,%0";
- }
- if (operands[2] == const1_rtx)
- return "movd %1,%0\n\taddd %0,%0";
+ {
+ if (GET_CODE (operands[0]) == REG)
+ {
+ if (GET_CODE (operands[1]) == REG)
+ {
+ if (REGNO (operands[0]) == REGNO (operands[1]))
+ {
+ if (operands[2] == const1_rtx)
+ return "addd %0,%0";
+ else if (INTVAL (operands[2]) == 2)
+ return "addd %0,%0\n\taddd %0,%0";
+ }
+ if (operands[2] == const1_rtx)
+ return "movd %1,%0\n\taddd %0,%0";
- operands[1] = gen_indexed_expr (const0_rtx, operands[1], operands[2]);
- return "addr %a1,%0";
- }
- if (operands[2] == const1_rtx)
- return "movd %1,%0\n\taddd %0,%0";
- }
- else if (GET_CODE (operands[1]) == REG)
- {
- operands[1] = gen_indexed_expr (const0_rtx, operands[1], operands[2]);
- return "addr %a1,%0";
- }
- else if (INTVAL (operands[2]) == 1
- && GET_CODE (operands[1]) == MEM
- && rtx_equal_p (operands [0], operands[1]))
- {
- rtx temp = XEXP (operands[1], 0);
+ operands[1] = gen_indexed_expr (const0_rtx, operands[1], operands[2]);
+ return "addr %a1,%0";
+ }
+ if (operands[2] == const1_rtx)
+ return "movd %1,%0\n\taddd %0,%0";
+ }
+ else if (GET_CODE (operands[1]) == REG)
+ {
+ operands[1] = gen_indexed_expr (const0_rtx, operands[1], operands[2]);
+ return "addr %a1,%0";
+ }
+ else if (INTVAL (operands[2]) == 1
+ && GET_CODE (operands[1]) == MEM
+ && rtx_equal_p (operands [0], operands[1]))
+ {
+ rtx temp = XEXP (operands[1], 0);
- if (GET_CODE (temp) == REG
- || (GET_CODE (temp) == PLUS
- && GET_CODE (XEXP (temp, 0)) == REG
- && GET_CODE (XEXP (temp, 1)) == CONST_INT))
- return "addd %0,%0";
- }
- else return "ashd %2,%0";
+ if (GET_CODE (temp) == REG
+ || (GET_CODE (temp) == PLUS
+ && GET_CODE (XEXP (temp, 0)) == REG
+ && GET_CODE (XEXP (temp, 1)) == CONST_INT))
+ return "addd %0,%0";
+ }
+ else return "ashd %2,%0";
+ }
return "ashd %2,%0";
}
+
+const char *
+output_move_dconst (n, s)
+ int n;
+ const char *s;
+{
+ static char r[32];
+
+ if (n > -9 && n < 8)
+ strcpy (r, "movqd ");
+ else if (n > 0 && n < 256)
+ strcpy (r, "movzbd ");
+ else if (n > 0 && n < 65536)
+ strcpy (r, "movzwd ");
+ else if (n < 0 && n > -129)
+ strcpy (r, "movxbd ");
+ else if (n < 0 && n > -32769)
+ strcpy (r, "movxwd ");
+ else
+ strcpy (r, "movd ");
+ strcat (r, s);
+ return r;
+}