/* Subroutines for insn-output.c for Motorola 68000 family.
- Copyright (C) 1987, 1993, 1994 Free Software Foundation, Inc.
+ Copyright (C) 1987, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2003
+ 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 m68k.md need these. */
-#include <stdio.h>
#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tm.h"
+#include "tree.h"
#include "rtl.h"
+#include "function.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 "recog.h"
+#include "toplev.h"
+#include "expr.h"
+#include "reload.h"
+#include "tm_p.h"
+#include "target.h"
+#include "target-def.h"
+#include "debug.h"
/* Needed for use_return_insn. */
#include "flags.h"
-#ifdef SUPPORT_SUN_FPA
-
-/* Index into this array by (register number >> 3) to find the
- smallest class which contains that register. */
-enum reg_class regno_reg_class[]
- = { DATA_REGS, ADDR_REGS, FP_REGS,
- LO_FPA_REGS, LO_FPA_REGS, FPA_REGS, FPA_REGS };
-
-#endif /* defined SUPPORT_SUN_FPA */
-
/* This flag is used to communicate between movhi and ASM_OUTPUT_CASE_END,
if SGS_SWITCH_TABLE. */
int switch_table_difference_label_flag;
-static rtx find_addr_reg ();
-rtx legitimize_pic_address ();
+static rtx find_addr_reg PARAMS ((rtx));
+static const char *singlemove_string PARAMS ((rtx *));
+static void m68k_output_function_prologue PARAMS ((FILE *, HOST_WIDE_INT));
+static void m68k_output_function_epilogue PARAMS ((FILE *, HOST_WIDE_INT));
+static void m68k_coff_asm_named_section PARAMS ((const char *, unsigned int));
+#ifdef CTOR_LIST_BEGIN
+static void m68k_svr3_asm_out_constructor PARAMS ((rtx, int));
+#endif
+#ifdef HPUX_ASM
+static void m68k_hp320_internal_label PARAMS ((FILE *, const char *, unsigned long));
+static void m68k_hp320_file_start PARAMS ((void));
+#endif
+static void m68k_output_mi_thunk PARAMS ((FILE *, tree, HOST_WIDE_INT,
+ HOST_WIDE_INT, tree));
+static int m68k_save_reg PARAMS ((unsigned int));
+static int const_int_cost PARAMS ((rtx));
+static bool m68k_rtx_costs PARAMS ((rtx, int, int, int *));
\f
-/* Emit a (use pic_offset_table_rtx) if we used PIC relocation in the
- function at any time during the compilation process. In the future
- we should try and eliminate the USE if we can easily determine that
- all PIC references were deleted from the current function. That would
- save an address register */
-
+/* Alignment to use for loops and jumps */
+/* Specify power of two alignment used for loops. */
+const char *m68k_align_loops_string;
+/* Specify power of two alignment used for non-loop jumps. */
+const char *m68k_align_jumps_string;
+/* Specify power of two alignment used for functions. */
+const char *m68k_align_funcs_string;
+
+/* Specify power of two alignment used for loops. */
+int m68k_align_loops;
+/* Specify power of two alignment used for non-loop jumps. */
+int m68k_align_jumps;
+/* Specify power of two alignment used for functions. */
+int m68k_align_funcs;
+
+/* Nonzero if the last compare/test insn had FP operands. The
+ sCC expanders peek at this to determine what to do for the
+ 68060, which has no fsCC instructions. */
+int m68k_last_compare_had_fp_operands;
+\f
+/* Initialize the GCC target structure. */
+
+#if INT_OP_GROUP == INT_OP_DOT_WORD
+#undef TARGET_ASM_ALIGNED_HI_OP
+#define TARGET_ASM_ALIGNED_HI_OP "\t.word\t"
+#endif
+
+#if INT_OP_GROUP == INT_OP_NO_DOT
+#undef TARGET_ASM_BYTE_OP
+#define TARGET_ASM_BYTE_OP "\tbyte\t"
+#undef TARGET_ASM_ALIGNED_HI_OP
+#define TARGET_ASM_ALIGNED_HI_OP "\tshort\t"
+#undef TARGET_ASM_ALIGNED_SI_OP
+#define TARGET_ASM_ALIGNED_SI_OP "\tlong\t"
+#endif
+
+#if INT_OP_GROUP == INT_OP_DC
+#undef TARGET_ASM_BYTE_OP
+#define TARGET_ASM_BYTE_OP "\tdc.b\t"
+#undef TARGET_ASM_ALIGNED_HI_OP
+#define TARGET_ASM_ALIGNED_HI_OP "\tdc.w\t"
+#undef TARGET_ASM_ALIGNED_SI_OP
+#define TARGET_ASM_ALIGNED_SI_OP "\tdc.l\t"
+#endif
+
+#undef TARGET_ASM_UNALIGNED_HI_OP
+#define TARGET_ASM_UNALIGNED_HI_OP TARGET_ASM_ALIGNED_HI_OP
+#undef TARGET_ASM_UNALIGNED_SI_OP
+#define TARGET_ASM_UNALIGNED_SI_OP TARGET_ASM_ALIGNED_SI_OP
+
+#undef TARGET_ASM_FUNCTION_PROLOGUE
+#define TARGET_ASM_FUNCTION_PROLOGUE m68k_output_function_prologue
+#undef TARGET_ASM_FUNCTION_EPILOGUE
+#define TARGET_ASM_FUNCTION_EPILOGUE m68k_output_function_epilogue
+#ifdef HPUX_ASM
+#undef TARGET_ASM_INTERNAL_LABEL
+#define TARGET_ASM_INTERNAL_LABEL m68k_hp320_internal_label
+#endif
+
+#undef TARGET_ASM_OUTPUT_MI_THUNK
+#define TARGET_ASM_OUTPUT_MI_THUNK m68k_output_mi_thunk
+#undef TARGET_ASM_CAN_OUTPUT_MI_THUNK
+#define TARGET_ASM_CAN_OUTPUT_MI_THUNK default_can_output_mi_thunk_no_vcall
+
+#undef TARGET_ASM_FILE_START_APP_OFF
+#define TARGET_ASM_FILE_START_APP_OFF true
+
+#undef TARGET_RTX_COSTS
+#define TARGET_RTX_COSTS m68k_rtx_costs
+
+struct gcc_target targetm = TARGET_INITIALIZER;
+\f
+/* Sometimes certain combinations of command options do not make
+ sense on a particular target machine. You can define a macro
+ `OVERRIDE_OPTIONS' to take account of this. This macro, if
+ defined, is executed once just after all the command options have
+ been parsed.
+
+ Don't use this macro to turn on various extra optimizations for
+ `-O'. That is what `OPTIMIZATION_OPTIONS' is for. */
+
void
-finalize_pic ()
+override_options ()
{
- if (flag_pic && current_function_uses_pic_offset_table)
- emit_insn (gen_rtx (USE, VOIDmode, pic_offset_table_rtx));
-}
+ int def_align;
+ int i;
+
+ def_align = 1;
+
+ /* Validate -malign-loops= value, or provide default */
+ m68k_align_loops = def_align;
+ if (m68k_align_loops_string)
+ {
+ i = atoi (m68k_align_loops_string);
+ if (i < 1 || i > MAX_CODE_ALIGN)
+ error ("-malign-loops=%d is not between 1 and %d", i, MAX_CODE_ALIGN);
+ else
+ m68k_align_loops = i;
+ }
+
+ /* Validate -malign-jumps= value, or provide default */
+ m68k_align_jumps = def_align;
+ if (m68k_align_jumps_string)
+ {
+ i = atoi (m68k_align_jumps_string);
+ if (i < 1 || i > MAX_CODE_ALIGN)
+ error ("-malign-jumps=%d is not between 1 and %d", i, MAX_CODE_ALIGN);
+ else
+ m68k_align_jumps = i;
+ }
+ /* Validate -malign-functions= value, or provide default */
+ m68k_align_funcs = def_align;
+ if (m68k_align_funcs_string)
+ {
+ i = atoi (m68k_align_funcs_string);
+ if (i < 1 || i > MAX_CODE_ALIGN)
+ error ("-malign-functions=%d is not between 1 and %d",
+ i, MAX_CODE_ALIGN);
+ else
+ m68k_align_funcs = i;
+ }
+
+ /* -fPIC uses 32-bit pc-relative displacements, which don't exist
+ until the 68020. */
+ if (! TARGET_68020 && flag_pic == 2)
+ error("-fPIC is not currently supported on the 68000 or 68010\n");
+
+ /* ??? A historic way of turning on pic, or is this intended to
+ be an embedded thing that doesn't have the same name binding
+ significance that it does on hosted ELF systems? */
+ if (TARGET_PCREL && flag_pic == 0)
+ flag_pic = 1;
+
+ /* Turn off function cse if we are doing PIC. We always want function call
+ to be done as `bsr foo@PLTPC', so it will force the assembler to create
+ the PLT entry for `foo'. Doing function cse will cause the address of
+ `foo' to be loaded into a register, which is exactly what we want to
+ avoid when we are doing PIC on svr4 m68k. */
+ if (flag_pic)
+ flag_no_function_cse = 1;
+
+ SUBTARGET_OVERRIDE_OPTIONS;
+
+ /* Tell the compiler which flavor of XFmode we're using. */
+ real_format_for_mode[XFmode - QFmode] = &ieee_extended_motorola_format;
+}
\f
+/* Return 1 if we need to save REGNO. */
+static int
+m68k_save_reg (regno)
+ unsigned int regno;
+{
+ if (flag_pic && current_function_uses_pic_offset_table
+ && regno == PIC_OFFSET_TABLE_REGNUM)
+ return 1;
+
+ if (current_function_calls_eh_return)
+ {
+ unsigned int i;
+ for (i = 0; ; i++)
+ {
+ unsigned int test = EH_RETURN_DATA_REGNO (i);
+ if (test == INVALID_REGNUM)
+ break;
+ if (test == regno)
+ return 1;
+ }
+ }
+
+ return (regs_ever_live[regno]
+ && !call_used_regs[regno]
+ && !fixed_regs[regno]
+ && !(regno == FRAME_POINTER_REGNUM && frame_pointer_needed));
+}
+
/* This function generates the assembly code for function entry.
STREAM is a stdio stream to output the code to.
SIZE is an int: how many units of temporary storage to allocate.
/* Note that the order of the bit mask for fmovem is the opposite
of the order for movem! */
-
-void
-output_function_prologue (stream, size)
+static void
+m68k_output_function_prologue (stream, size)
FILE *stream;
- int size;
+ HOST_WIDE_INT size;
{
register int regno;
register int mask = 0;
int num_saved_regs = 0;
- extern char call_used_regs[];
- int fsize = (size + 3) & -4;
+ HOST_WIDE_INT fsize = (size + 3) & -4;
+ HOST_WIDE_INT cfa_offset = INCOMING_FRAME_SP_OFFSET;
+ HOST_WIDE_INT cfa_store_offset = cfa_offset;
+ /* If the stack limit is a symbol, we can check it here,
+ before actually allocating the space. */
+ if (current_function_limit_stack
+ && GET_CODE (stack_limit_rtx) == SYMBOL_REF)
+ {
+#if defined (MOTOROLA)
+ asm_fprintf (stream, "\tcmp.l %I%s+%wd,%Rsp\n\ttrapcs\n",
+ XSTR (stack_limit_rtx, 0), fsize + 4);
+#else
+ asm_fprintf (stream, "\tcmpl %I%s+%wd,%Rsp\n\ttrapcs\n",
+ XSTR (stack_limit_rtx, 0), fsize + 4);
+#endif
+ }
if (frame_pointer_needed)
{
{
/* on the 68040, pea + move is faster than link.w 0 */
#ifdef MOTOROLA
- asm_fprintf (stream, "\tpea (%s)\n\tmove.l %s,%s\n",
- reg_names[FRAME_POINTER_REGNUM], reg_names[STACK_POINTER_REGNUM],
- reg_names[FRAME_POINTER_REGNUM]);
+ fprintf (stream, "\tpea (%s)\n\tmove.l %s,%s\n",
+ reg_names[FRAME_POINTER_REGNUM],
+ reg_names[STACK_POINTER_REGNUM],
+ reg_names[FRAME_POINTER_REGNUM]);
#else
- asm_fprintf (stream, "\tpea %s@\n\tmovel %s,%s\n",
- reg_names[FRAME_POINTER_REGNUM], reg_names[STACK_POINTER_REGNUM],
- reg_names[FRAME_POINTER_REGNUM]);
+ fprintf (stream, "\tpea %s@\n\tmovel %s,%s\n",
+ reg_names[FRAME_POINTER_REGNUM],
+ reg_names[STACK_POINTER_REGNUM],
+ reg_names[FRAME_POINTER_REGNUM]);
#endif
}
else if (fsize < 0x8000)
{
#ifdef MOTOROLA
- asm_fprintf (stream, "\tlink.w %s,%0I%d\n",
+ asm_fprintf (stream, "\tlink.w %s,%I%wd\n",
reg_names[FRAME_POINTER_REGNUM], -fsize);
#else
- asm_fprintf (stream, "\tlink %s,%0I%d\n",
+ asm_fprintf (stream, "\tlink %s,%I%wd\n",
reg_names[FRAME_POINTER_REGNUM], -fsize);
#endif
}
else if (TARGET_68020)
{
#ifdef MOTOROLA
- asm_fprintf (stream, "\tlink.l %s,%0I%d\n",
+ asm_fprintf (stream, "\tlink.l %s,%I%wd\n",
reg_names[FRAME_POINTER_REGNUM], -fsize);
#else
- asm_fprintf (stream, "\tlink %s,%0I%d\n",
+ asm_fprintf (stream, "\tlink %s,%I%wd\n",
reg_names[FRAME_POINTER_REGNUM], -fsize);
#endif
}
{
/* Adding negative number is faster on the 68040. */
#ifdef MOTOROLA
- asm_fprintf (stream, "\tlink.w %s,%0I0\n\tadd.l %0I%d,%Rsp\n",
+ asm_fprintf (stream, "\tlink.w %s,%I0\n\tadd.l %I%wd,%Rsp\n",
reg_names[FRAME_POINTER_REGNUM], -fsize);
#else
- asm_fprintf (stream, "\tlink %s,%0I0\n\taddl %0I%d,%Rsp\n",
+ asm_fprintf (stream, "\tlink %s,%I0\n\taddl %I%wd,%Rsp\n",
reg_names[FRAME_POINTER_REGNUM], -fsize);
#endif
}
+ if (dwarf2out_do_frame ())
+ {
+ char *l;
+ l = (char *) dwarf2out_cfi_label ();
+ cfa_store_offset += 4;
+ cfa_offset = cfa_store_offset;
+ dwarf2out_reg_save (l, FRAME_POINTER_REGNUM, -cfa_store_offset);
+ dwarf2out_def_cfa (l, FRAME_POINTER_REGNUM, cfa_offset);
+ cfa_store_offset += fsize;
+ }
}
else if (fsize)
{
- /* Adding negative number is faster on the 68040. */
if (fsize + 4 < 0x8000)
{
- /* asm_fprintf() cannot handle %. */
+ if (fsize + 4 <= 8)
+ {
+ if (!TARGET_COLDFIRE)
+ {
+ /* asm_fprintf() cannot handle %. */
+#ifdef MOTOROLA
+ asm_fprintf (stream, "\tsubq.w %I%wd,%Rsp\n", fsize + 4);
+#else
+ asm_fprintf (stream, "\tsubqw %I%wd,%Rsp\n", fsize + 4);
+#endif
+ }
+ else
+ {
+ /* asm_fprintf() cannot handle %. */
+#ifdef MOTOROLA
+ asm_fprintf (stream, "\tsubq.l %I%wd,%Rsp\n", fsize + 4);
+#else
+ asm_fprintf (stream, "\tsubql %I%wd,%Rsp\n", fsize + 4);
+#endif
+ }
+ }
+ else if (fsize + 4 <= 16 && TARGET_CPU32)
+ {
+ /* On the CPU32 it is faster to use two subqw instructions to
+ subtract a small integer (8 < N <= 16) to a register. */
+ /* asm_fprintf() cannot handle %. */
#ifdef MOTOROLA
- asm_fprintf (stream, "\tadd.w %0I%d,%Rsp\n", - (fsize + 4));
+ asm_fprintf (stream,
+ "\tsubq.w %I8,%Rsp\n\tsubq.w %I%wd,%Rsp\n",
+ fsize + 4 - 8);
#else
- asm_fprintf (stream, "\taddw %0I%d,%Rsp\n", - (fsize + 4));
+ asm_fprintf (stream, "\tsubqw %I8,%Rsp\n\tsubqw %I%wd,%Rsp\n",
+ fsize + 4 - 8);
#endif
+ }
+ else if (TARGET_68040)
+ {
+ /* Adding negative number is faster on the 68040. */
+ /* asm_fprintf() cannot handle %. */
+#ifdef MOTOROLA
+ asm_fprintf (stream, "\tadd.w %I%wd,%Rsp\n", - (fsize + 4));
+#else
+ asm_fprintf (stream, "\taddw %I%wd,%Rsp\n", - (fsize + 4));
+#endif
+ }
+ else
+ {
+#ifdef MOTOROLA
+ asm_fprintf (stream, "\tlea (%wd,%Rsp),%Rsp\n", - (fsize + 4));
+#else
+ asm_fprintf (stream, "\tlea %Rsp@(%wd),%Rsp\n", - (fsize + 4));
+#endif
+ }
}
else
{
- /* asm_fprintf() cannot handle %. */
+ /* asm_fprintf() cannot handle %. */
#ifdef MOTOROLA
- asm_fprintf (stream, "\tadd.l %0I%d,%Rsp\n", - (fsize + 4));
+ asm_fprintf (stream, "\tadd.l %I%wd,%Rsp\n", - (fsize + 4));
#else
- asm_fprintf (stream, "\taddl %0I%d,%Rsp\n", - (fsize + 4));
+ asm_fprintf (stream, "\taddl %I%wd,%Rsp\n", - (fsize + 4));
#endif
}
+ if (dwarf2out_do_frame ())
+ {
+ cfa_store_offset += fsize + 4;
+ cfa_offset = cfa_store_offset;
+ dwarf2out_def_cfa ("", STACK_POINTER_REGNUM, cfa_offset);
+ }
}
-#ifdef SUPPORT_SUN_FPA
- for (regno = 24; regno < 56; regno++)
- if (regs_ever_live[regno] && ! call_used_regs[regno])
- {
-#ifdef MOTOROLA
- asm_fprintf (stream, "\tfpmovd %s,-(%Rsp)\n",
- reg_names[regno]);
-#else
- asm_fprintf (stream, "\tfpmoved %s,%Rsp@-\n",
- reg_names[regno]);
-#endif
- }
-#endif
- for (regno = 16; regno < 24; regno++)
- if (regs_ever_live[regno] && ! call_used_regs[regno])
- mask |= 1 << (regno - 16);
- if ((mask & 0xff) != 0)
+ if (TARGET_68881)
{
+ for (regno = 16; regno < 24; regno++)
+ if (m68k_save_reg (regno))
+ {
+ mask |= 1 << (regno - 16);
+ num_saved_regs++;
+ }
+ if ((mask & 0xff) != 0)
+ {
#ifdef MOTOROLA
- asm_fprintf (stream, "\tfmovm %0I0x%x,-(%Rsp)\n", mask & 0xff);
+ asm_fprintf (stream, "\tfmovm %I0x%x,-(%Rsp)\n", mask & 0xff);
#else
- asm_fprintf (stream, "\tfmovem %0I0x%x,%Rsp@-\n", mask & 0xff);
+ asm_fprintf (stream, "\tfmovem %I0x%x,%Rsp@-\n", mask & 0xff);
#endif
+ if (dwarf2out_do_frame ())
+ {
+ char *l = (char *) dwarf2out_cfi_label ();
+ int n_regs;
+
+ cfa_store_offset += num_saved_regs * 12;
+ if (! frame_pointer_needed)
+ {
+ cfa_offset = cfa_store_offset;
+ dwarf2out_def_cfa (l, STACK_POINTER_REGNUM, cfa_offset);
+ }
+ for (regno = 16, n_regs = 0; regno < 24; regno++)
+ if (mask & (1 << (regno - 16)))
+ dwarf2out_reg_save (l, regno,
+ -cfa_store_offset + n_regs++ * 12);
+ }
+ }
+ mask = 0;
+ num_saved_regs = 0;
}
- mask = 0;
for (regno = 0; regno < 16; regno++)
- if (regs_ever_live[regno] && ! call_used_regs[regno])
+ if (m68k_save_reg (regno))
{
mask |= 1 << (15 - regno);
num_saved_regs++;
}
- if (frame_pointer_needed)
- {
- mask &= ~ (1 << (15 - FRAME_POINTER_REGNUM));
- num_saved_regs--;
- }
-#if NEED_PROBE
- fprintf (stream, "\ttstl sp@(%d)\n", NEED_PROBE - num_saved_regs * 4);
+ /* If the stack limit is not a symbol, check it here.
+ This has the disadvantage that it may be too late... */
+ if (current_function_limit_stack)
+ {
+ if (REG_P (stack_limit_rtx))
+ {
+#if defined (MOTOROLA)
+ asm_fprintf (stream, "\tcmp.l %s,%Rsp\n\ttrapcs\n",
+ reg_names[REGNO (stack_limit_rtx)]);
+#else
+ asm_fprintf (stream, "\tcmpl %s,%Rsp\n\ttrapcs\n",
+ reg_names[REGNO (stack_limit_rtx)]);
#endif
-
+ }
+ else if (GET_CODE (stack_limit_rtx) != SYMBOL_REF)
+ warning ("stack limit expression is not supported");
+ }
+
if (num_saved_regs <= 2)
{
/* Store each separately in the same order moveml uses.
int i;
- /* Undo the work from above. */
+ /* Undo the work from above. */
for (i = 0; i< 16; i++)
if (mask & (1 << i))
- asm_fprintf (stream,
+ {
+ asm_fprintf (stream,
#ifdef MOTOROLA
- "\t%Omove.l %s,-(%Rsp)\n",
+ "\t%Omove.l %s,-(%Rsp)\n",
#else
- "\tmovel %s,%Rsp@-\n",
+ "\tmovel %s,%Rsp@-\n",
#endif
- reg_names[15 - i]);
+ reg_names[15 - i]);
+ if (dwarf2out_do_frame ())
+ {
+ char *l = (char *) dwarf2out_cfi_label ();
+
+ cfa_store_offset += 4;
+ if (! frame_pointer_needed)
+ {
+ cfa_offset = cfa_store_offset;
+ dwarf2out_def_cfa (l, STACK_POINTER_REGNUM, cfa_offset);
+ }
+ dwarf2out_reg_save (l, 15 - i, -cfa_store_offset);
+ }
+ }
}
else if (mask)
{
+ if (TARGET_COLDFIRE)
+ {
+ /* The coldfire does not support the predecrement form of the
+ movml instruction, so we must adjust the stack pointer and
+ then use the plain address register indirect mode. We also
+ have to invert the register save mask to use the new mode.
+
+ FIXME: if num_saved_regs was calculated earlier, we could
+ combine the stack pointer adjustment with any adjustment
+ done when the initial stack frame is created. This would
+ save an instruction */
+
+ int newmask = 0;
+ int i;
+
+ for (i = 0; i < 16; i++)
+ if (mask & (1 << i))
+ newmask |= (1 << (15-i));
+
+#ifdef MOTOROLA
+ asm_fprintf (stream, "\tlea (%d,%Rsp),%Rsp\n", -num_saved_regs*4);
+ asm_fprintf (stream, "\tmovm.l %I0x%x,(%Rsp)\n", newmask);
+#else
+ asm_fprintf (stream, "\tlea %Rsp@(%d),%Rsp\n", -num_saved_regs*4);
+ asm_fprintf (stream, "\tmoveml %I0x%x,%Rsp@\n", newmask);
+#endif
+ }
+ else
+ {
#ifdef MOTOROLA
- asm_fprintf (stream, "\tmovm.l %0I0x%x,-(%Rsp)\n", mask);
+ asm_fprintf (stream, "\tmovm.l %I0x%x,-(%Rsp)\n", mask);
#else
- asm_fprintf (stream, "\tmoveml %0I0x%x,%Rsp@-\n", mask);
+ asm_fprintf (stream, "\tmoveml %I0x%x,%Rsp@-\n", mask);
#endif
+ }
+ if (dwarf2out_do_frame ())
+ {
+ char *l = (char *) dwarf2out_cfi_label ();
+ int n_regs;
+
+ cfa_store_offset += num_saved_regs * 4;
+ if (! frame_pointer_needed)
+ {
+ cfa_offset = cfa_store_offset;
+ dwarf2out_def_cfa (l, STACK_POINTER_REGNUM, cfa_offset);
+ }
+ for (regno = 0, n_regs = 0; regno < 16; regno++)
+ if (mask & (1 << (15 - regno)))
+ dwarf2out_reg_save (l, regno,
+ -cfa_store_offset + n_regs++ * 4);
+ }
}
if (flag_pic && current_function_uses_pic_offset_table)
{
asm_fprintf (stream, "\t%Olea (%Rpc, %U_GLOBAL_OFFSET_TABLE_@GOTPC), %s\n",
reg_names[PIC_OFFSET_TABLE_REGNUM]);
#else
- asm_fprintf (stream, "\tmovel %0I__GLOBAL_OFFSET_TABLE_, %s\n",
+ asm_fprintf (stream, "\tmovel %I%U_GLOBAL_OFFSET_TABLE_, %s\n",
reg_names[PIC_OFFSET_TABLE_REGNUM]);
asm_fprintf (stream, "\tlea %Rpc@(0,%s:l),%s\n",
reg_names[PIC_OFFSET_TABLE_REGNUM],
if (!reload_completed || frame_pointer_needed || get_frame_size () != 0)
return 0;
- /* Copied from output_function_epilogue (). We should probably create a
- separate layout routine to perform the common work. */
-
- for (regno = 0 ; regno < FIRST_PSEUDO_REGISTER ; regno++)
- if (regs_ever_live[regno] && ! call_used_regs[regno])
+ for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
+ if (m68k_save_reg (regno))
return 0;
-
+
return 1;
}
/* This function generates the assembly code for function exit,
- on machines that need it. Args are same as for FUNCTION_PROLOGUE.
+ on machines that need it.
The function epilogue should not depend on the current stack pointer!
It should use the frame pointer only, if there is a frame pointer.
This is mandatory because of alloca; we also take advantage of it to
omit stack adjustments before returning. */
-void
-output_function_epilogue (stream, size)
+static void
+m68k_output_function_epilogue (stream, size)
FILE *stream;
- int size;
+ HOST_WIDE_INT size;
{
register int regno;
register int mask, fmask;
register int nregs;
- int offset, foffset, fpoffset;
- extern char call_used_regs[];
- int fsize = (size + 3) & -4;
+ HOST_WIDE_INT offset, foffset;
+ HOST_WIDE_INT fsize = (size + 3) & -4;
int big = 0;
rtx insn = get_last_insn ();
+ int restore_from_sp = 0;
/* If the last insn was a BARRIER, we don't have to write any code. */
if (GET_CODE (insn) == NOTE)
{
/* Output just a no-op so that debuggers don't get confused
about which function the pc is in at this address. */
- asm_fprintf (stream, "\tnop\n");
+ fprintf (stream, "\tnop\n");
return;
}
#ifdef FUNCTION_EXTRA_EPILOGUE
FUNCTION_EXTRA_EPILOGUE (stream, size);
#endif
- nregs = 0; fmask = 0; fpoffset = 0;
-#ifdef SUPPORT_SUN_FPA
- for (regno = 24 ; regno < 56 ; regno++)
- if (regs_ever_live[regno] && ! call_used_regs[regno])
- nregs++;
- fpoffset = nregs * 8;
-#endif
- nregs = 0;
- for (regno = 16; regno < 24; regno++)
- if (regs_ever_live[regno] && ! call_used_regs[regno])
- {
- nregs++;
- fmask |= 1 << (23 - regno);
- }
- foffset = fpoffset + nregs * 12;
+ nregs = 0; fmask = 0;
+ if (TARGET_68881)
+ {
+ for (regno = 16; regno < 24; regno++)
+ if (m68k_save_reg (regno))
+ {
+ nregs++;
+ fmask |= 1 << (23 - regno);
+ }
+ }
+ foffset = nregs * 12;
nregs = 0; mask = 0;
- if (frame_pointer_needed)
- regs_ever_live[FRAME_POINTER_REGNUM] = 0;
for (regno = 0; regno < 16; regno++)
- if (regs_ever_live[regno] && ! call_used_regs[regno])
+ if (m68k_save_reg (regno))
{
nregs++;
mask |= 1 << regno;
}
offset = foffset + nregs * 4;
+ /* FIXME : leaf_function_p below is too strong.
+ What we really need to know there is if there could be pending
+ stack adjustment needed at that point. */
+ restore_from_sp = ! frame_pointer_needed
+ || (! current_function_calls_alloca && leaf_function_p ());
if (offset + fsize >= 0x8000
- && frame_pointer_needed
- && (mask || fmask || fpoffset))
+ && ! restore_from_sp
+ && (mask || fmask))
{
#ifdef MOTOROLA
- asm_fprintf (stream, "\t%Omove.l %0I%d,%Ra1\n", -fsize);
+ asm_fprintf (stream, "\t%Omove.l %I%wd,%Ra1\n", -fsize);
#else
- asm_fprintf (stream, "\tmovel %0I%d,%Ra1\n", -fsize);
+ asm_fprintf (stream, "\tmovel %I%wd,%Ra1\n", -fsize);
#endif
fsize = 0, big = 1;
}
- if (nregs <= 2)
+ if (TARGET_COLDFIRE || nregs <= 2)
{
/* Restore each separately in the same order moveml does.
Using two movel instructions instead of a single moveml
is about 15% faster for the 68020 and 68030 at no expense
- in code size. */
+ in code size. */
int i;
- /* Undo the work from above. */
+ /* Undo the work from above. */
for (i = 0; i< 16; i++)
if (mask & (1 << i))
{
if (big)
{
#ifdef MOTOROLA
- asm_fprintf (stream, "\t%Omove.l -%d(%s,%Ra1.l),%s\n",
+ asm_fprintf (stream, "\t%Omove.l -%wd(%s,%Ra1.l),%s\n",
offset + fsize,
reg_names[FRAME_POINTER_REGNUM],
reg_names[i]);
#else
- asm_fprintf (stream, "\tmovel %s@(-%d,%Ra1:l),%s\n",
+ asm_fprintf (stream, "\tmovel %s@(-%wd,%Ra1:l),%s\n",
reg_names[FRAME_POINTER_REGNUM],
offset + fsize, reg_names[i]);
#endif
}
- else if (! frame_pointer_needed)
+ else if (restore_from_sp)
{
#ifdef MOTOROLA
asm_fprintf (stream, "\t%Omove.l (%Rsp)+,%s\n",
else
{
#ifdef MOTOROLA
- asm_fprintf (stream, "\t%Omove.l -%d(%s),%s\n",
+ asm_fprintf (stream, "\t%Omove.l -%wd(%s),%s\n",
offset + fsize,
reg_names[FRAME_POINTER_REGNUM],
reg_names[i]);
#else
- asm_fprintf (stream, "\tmovel %s@(-%d),%s\n",
+ asm_fprintf (stream, "\tmovel %s@(-%wd),%s\n",
reg_names[FRAME_POINTER_REGNUM],
offset + fsize, reg_names[i]);
#endif
if (big)
{
#ifdef MOTOROLA
- asm_fprintf (stream, "\tmovm.l -%d(%s,%Ra1.l),%0I0x%x\n",
+ asm_fprintf (stream, "\tmovm.l -%wd(%s,%Ra1.l),%I0x%x\n",
offset + fsize,
reg_names[FRAME_POINTER_REGNUM],
mask);
#else
- asm_fprintf (stream, "\tmoveml %s@(-%d,%Ra1:l),%0I0x%x\n",
+ asm_fprintf (stream, "\tmoveml %s@(-%wd,%Ra1:l),%I0x%x\n",
reg_names[FRAME_POINTER_REGNUM],
offset + fsize, mask);
#endif
}
- else if (! frame_pointer_needed)
+ else if (restore_from_sp)
{
#ifdef MOTOROLA
- asm_fprintf (stream, "\tmovm.l (%Rsp)+,%0I0x%x\n", mask);
+ asm_fprintf (stream, "\tmovm.l (%Rsp)+,%I0x%x\n", mask);
#else
- asm_fprintf (stream, "\tmoveml %Rsp@+,%0I0x%x\n", mask);
+ asm_fprintf (stream, "\tmoveml %Rsp@+,%I0x%x\n", mask);
#endif
}
else
{
#ifdef MOTOROLA
- asm_fprintf (stream, "\tmovm.l -%d(%s),%0I0x%x\n",
+ asm_fprintf (stream, "\tmovm.l -%wd(%s),%I0x%x\n",
offset + fsize,
reg_names[FRAME_POINTER_REGNUM],
mask);
#else
- asm_fprintf (stream, "\tmoveml %s@(-%d),%0I0x%x\n",
+ asm_fprintf (stream, "\tmoveml %s@(-%wd),%I0x%x\n",
reg_names[FRAME_POINTER_REGNUM],
offset + fsize, mask);
#endif
if (big)
{
#ifdef MOTOROLA
- asm_fprintf (stream, "\tfmovm -%d(%s,%Ra1.l),%0I0x%x\n",
+ asm_fprintf (stream, "\tfmovm -%wd(%s,%Ra1.l),%I0x%x\n",
foffset + fsize,
reg_names[FRAME_POINTER_REGNUM],
fmask);
#else
- asm_fprintf (stream, "\tfmovem %s@(-%d,%Ra1:l),%0I0x%x\n",
+ asm_fprintf (stream, "\tfmovem %s@(-%wd,%Ra1:l),%I0x%x\n",
reg_names[FRAME_POINTER_REGNUM],
foffset + fsize, fmask);
#endif
}
- else if (! frame_pointer_needed)
+ else if (restore_from_sp)
{
#ifdef MOTOROLA
- asm_fprintf (stream, "\tfmovm (%Rsp)+,%0I0x%x\n", fmask);
+ asm_fprintf (stream, "\tfmovm (%Rsp)+,%I0x%x\n", fmask);
#else
- asm_fprintf (stream, "\tfmovem %Rsp@+,%0I0x%x\n", fmask);
+ asm_fprintf (stream, "\tfmovem %Rsp@+,%I0x%x\n", fmask);
#endif
}
else
{
#ifdef MOTOROLA
- asm_fprintf (stream, "\tfmovm -%d(%s),%0I0x%x\n",
+ asm_fprintf (stream, "\tfmovm -%wd(%s),%I0x%x\n",
foffset + fsize,
reg_names[FRAME_POINTER_REGNUM],
fmask);
#else
- asm_fprintf (stream, "\tfmovem %s@(-%d),%0I0x%x\n",
+ asm_fprintf (stream, "\tfmovem %s@(-%wd),%I0x%x\n",
reg_names[FRAME_POINTER_REGNUM],
foffset + fsize, fmask);
#endif
}
}
- if (fpoffset != 0)
- for (regno = 55; regno >= 24; regno--)
- if (regs_ever_live[regno] && ! call_used_regs[regno])
- {
- if (big)
+ if (frame_pointer_needed)
+ fprintf (stream, "\tunlk %s\n",
+ reg_names[FRAME_POINTER_REGNUM]);
+ else if (fsize)
+ {
+ if (fsize + 4 <= 8)
+ {
+ if (!TARGET_COLDFIRE)
{
#ifdef MOTOROLA
- asm_fprintf (stream, "\tfpmovd -%d(%s,%Ra1.l), %s\n",
- fpoffset + fsize,
- reg_names[FRAME_POINTER_REGNUM],
- reg_names[regno]);
+ asm_fprintf (stream, "\taddq.w %I%wd,%Rsp\n", fsize + 4);
#else
- asm_fprintf (stream, "\tfpmoved %s@(-%d,%Ra1:l), %s\n",
- reg_names[FRAME_POINTER_REGNUM],
- fpoffset + fsize, reg_names[regno]);
+ asm_fprintf (stream, "\taddqw %I%wd,%Rsp\n", fsize + 4);
#endif
}
- else if (! frame_pointer_needed)
+ else
{
#ifdef MOTOROLA
- asm_fprintf (stream, "\tfpmovd (%Rsp)+,%s\n",
- reg_names[regno]);
+ asm_fprintf (stream, "\taddq.l %I%wd,%Rsp\n", fsize + 4);
#else
- asm_fprintf (stream, "\tfpmoved %Rsp@+, %s\n",
- reg_names[regno]);
+ asm_fprintf (stream, "\taddql %I%wd,%Rsp\n", fsize + 4);
#endif
}
- else
- {
+ }
+ else if (fsize + 4 <= 16 && TARGET_CPU32)
+ {
+ /* On the CPU32 it is faster to use two addqw instructions to
+ add a small integer (8 < N <= 16) to a register. */
+ /* asm_fprintf() cannot handle %. */
#ifdef MOTOROLA
- asm_fprintf (stream, "\tfpmovd -%d(%s), %s\n",
- fpoffset + fsize,
- reg_names[FRAME_POINTER_REGNUM],
- reg_names[regno]);
+ asm_fprintf (stream, "\taddq.w %I8,%Rsp\n\taddq.w %I%wd,%Rsp\n",
+ fsize + 4 - 8);
#else
- asm_fprintf (stream, "\tfpmoved %s@(-%d), %s\n",
- reg_names[FRAME_POINTER_REGNUM],
- fpoffset + fsize, reg_names[regno]);
+ asm_fprintf (stream, "\taddqw %I8,%Rsp\n\taddqw %I%wd,%Rsp\n",
+ fsize + 4 - 8);
#endif
- }
- fpoffset -= 8;
}
- if (frame_pointer_needed)
- fprintf (stream, "\tunlk %s\n",
- reg_names[FRAME_POINTER_REGNUM]);
- else if (fsize)
- {
- if (fsize + 4 < 0x8000)
+ else if (fsize + 4 < 0x8000)
{
- /* asm_fprintf() cannot handle %. */
+ if (TARGET_68040)
+ {
+ /* asm_fprintf() cannot handle %. */
+#ifdef MOTOROLA
+ asm_fprintf (stream, "\tadd.w %I%wd,%Rsp\n", fsize + 4);
+#else
+ asm_fprintf (stream, "\taddw %I%wd,%Rsp\n", fsize + 4);
+#endif
+ }
+ else
+ {
#ifdef MOTOROLA
- asm_fprintf (stream, "\tadd.w %0I%d,%Rsp\n", fsize + 4);
+ asm_fprintf (stream, "\tlea (%wd,%Rsp),%Rsp\n", fsize + 4);
#else
- asm_fprintf (stream, "\taddw %0I%d,%Rsp\n", fsize + 4);
+ asm_fprintf (stream, "\tlea %Rsp@(%wd),%Rsp\n", fsize + 4);
#endif
+ }
}
else
{
- /* asm_fprintf() cannot handle %. */
+ /* asm_fprintf() cannot handle %. */
#ifdef MOTOROLA
- asm_fprintf (stream, "\tadd.l %0I%d,%Rsp\n", fsize + 4);
+ asm_fprintf (stream, "\tadd.l %I%wd,%Rsp\n", fsize + 4);
#else
- asm_fprintf (stream, "\taddl %0I%d,%Rsp\n", fsize + 4);
+ asm_fprintf (stream, "\taddl %I%wd,%Rsp\n", fsize + 4);
#endif
}
}
+ if (current_function_calls_eh_return)
+ {
+#ifdef MOTOROLA
+ asm_fprintf (stream, "\tadd.l %Ra0,%Rsp\n");
+#else
+ asm_fprintf (stream, "\taddl %Ra0,%Rsp\n");
+#endif
+ }
if (current_function_pops_args)
- asm_fprintf (stream, "\trtd %0I%d\n", current_function_pops_args);
+ asm_fprintf (stream, "\trtd %I%d\n", current_function_pops_args);
else
fprintf (stream, "\trts\n");
}
register rtx op;
enum machine_mode mode;
{
- return op != stack_pointer_rtx && general_operand (op, mode);
+ return op != stack_pointer_rtx && nonimmediate_operand (op, mode);
}
/* Return TRUE if X is a valid comparison operator for the dbcc
int
valid_dbcc_comparison_p (x, mode)
rtx x;
- enum machine_mode mode;
+ enum machine_mode mode ATTRIBUTE_UNUSED;
{
- /* We could add support for these in the future */
- if (cc_prev_status.flags & CC_IN_68881)
- return 0;
-
switch (GET_CODE (x))
{
-
case EQ: case NE: case GTU: case LTU:
case GEU: case LEU:
return 1;
}
}
+/* Return nonzero if flags are currently in the 68881 flag register. */
+int
+flags_in_68881 ()
+{
+ /* We could add support for these in the future */
+ return cc_status.flags & CC_IN_68881;
+}
+
/* Output a dbCC; jCC sequence. Note we do not handle the
floating point version of this sequence (Fdbcc). We also
do not handle alternative conditions when CC_NO_OVERFLOW is
- set. It is assumed that valid_dbcc_comparison_p will kick
- those out before we get here. */
+ set. It is assumed that valid_dbcc_comparison_p and flags_in_68881 will
+ kick those out before we get here. */
+void
output_dbcc_and_branch (operands)
rtx *operands;
{
-
switch (GET_CODE (operands[3]))
{
case EQ:
}
/* If the decrement is to be done in SImode, then we have
- to compensate for the fact that dbcc decrements in HImode. */
+ to compensate for the fact that dbcc decrements in HImode. */
switch (GET_MODE (operands[0]))
{
case SImode:
}
}
-char *
+const char *
+output_scc_di(op, operand1, operand2, dest)
+ rtx op;
+ rtx operand1;
+ rtx operand2;
+ rtx dest;
+{
+ rtx loperands[7];
+ enum rtx_code op_code = GET_CODE (op);
+
+ /* This does not produce a useful cc. */
+ CC_STATUS_INIT;
+
+ /* The m68k cmp.l instruction requires operand1 to be a reg as used
+ below. Swap the operands and change the op if these requirements
+ are not fulfilled. */
+ if (GET_CODE (operand2) == REG && GET_CODE (operand1) != REG)
+ {
+ rtx tmp = operand1;
+
+ operand1 = operand2;
+ operand2 = tmp;
+ op_code = swap_condition (op_code);
+ }
+ loperands[0] = operand1;
+ if (GET_CODE (operand1) == REG)
+ loperands[1] = gen_rtx_REG (SImode, REGNO (operand1) + 1);
+ else
+ loperands[1] = adjust_address (operand1, SImode, 4);
+ if (operand2 != const0_rtx)
+ {
+ loperands[2] = operand2;
+ if (GET_CODE (operand2) == REG)
+ loperands[3] = gen_rtx_REG (SImode, REGNO (operand2) + 1);
+ else
+ loperands[3] = adjust_address (operand2, SImode, 4);
+ }
+ loperands[4] = gen_label_rtx();
+ if (operand2 != const0_rtx)
+ {
+#ifdef MOTOROLA
+#ifdef SGS_CMP_ORDER
+ output_asm_insn ("cmp%.l %0,%2\n\tjbne %l4\n\tcmp%.l %1,%3", loperands);
+#else
+ output_asm_insn ("cmp%.l %2,%0\n\tjbne %l4\n\tcmp%.l %3,%1", loperands);
+#endif
+#else
+#ifdef SGS_CMP_ORDER
+ output_asm_insn ("cmp%.l %0,%2\n\tjne %l4\n\tcmp%.l %1,%3", loperands);
+#else
+ output_asm_insn ("cmp%.l %2,%0\n\tjne %l4\n\tcmp%.l %3,%1", loperands);
+#endif
+#endif
+ }
+ else
+ {
+ if (TARGET_68020 || TARGET_COLDFIRE || ! ADDRESS_REG_P (loperands[0]))
+ output_asm_insn ("tst%.l %0", loperands);
+ else
+ {
+#ifdef SGS_CMP_ORDER
+ output_asm_insn ("cmp%.w %0,%#0", loperands);
+#else
+ output_asm_insn ("cmp%.w %#0,%0", loperands);
+#endif
+ }
+
+#ifdef MOTOROLA
+ output_asm_insn ("jbne %l4", loperands);
+#else
+ output_asm_insn ("jne %l4", loperands);
+#endif
+
+ if (TARGET_68020 || TARGET_COLDFIRE || ! ADDRESS_REG_P (loperands[1]))
+ output_asm_insn ("tst%.l %1", loperands);
+ else
+ {
+#ifdef SGS_CMP_ORDER
+ output_asm_insn ("cmp%.w %1,%#0", loperands);
+#else
+ output_asm_insn ("cmp%.w %#0,%1", loperands);
+#endif
+ }
+ }
+
+ loperands[5] = dest;
+
+ switch (op_code)
+ {
+ case EQ:
+ (*targetm.asm_out.internal_label) (asm_out_file, "L",
+ CODE_LABEL_NUMBER (loperands[4]));
+ output_asm_insn ("seq %5", loperands);
+ break;
+
+ case NE:
+ (*targetm.asm_out.internal_label) (asm_out_file, "L",
+ CODE_LABEL_NUMBER (loperands[4]));
+ output_asm_insn ("sne %5", loperands);
+ break;
+
+ case GT:
+ loperands[6] = gen_label_rtx();
+#ifdef MOTOROLA
+ output_asm_insn ("shi %5\n\tjbra %l6", loperands);
+#else
+ output_asm_insn ("shi %5\n\tjra %l6", loperands);
+#endif
+ (*targetm.asm_out.internal_label) (asm_out_file, "L",
+ CODE_LABEL_NUMBER (loperands[4]));
+ output_asm_insn ("sgt %5", loperands);
+ (*targetm.asm_out.internal_label) (asm_out_file, "L",
+ CODE_LABEL_NUMBER (loperands[6]));
+ break;
+
+ case GTU:
+ (*targetm.asm_out.internal_label) (asm_out_file, "L",
+ CODE_LABEL_NUMBER (loperands[4]));
+ output_asm_insn ("shi %5", loperands);
+ break;
+
+ case LT:
+ loperands[6] = gen_label_rtx();
+#ifdef MOTOROLA
+ output_asm_insn ("scs %5\n\tjbra %l6", loperands);
+#else
+ output_asm_insn ("scs %5\n\tjra %l6", loperands);
+#endif
+ (*targetm.asm_out.internal_label) (asm_out_file, "L",
+ CODE_LABEL_NUMBER (loperands[4]));
+ output_asm_insn ("slt %5", loperands);
+ (*targetm.asm_out.internal_label) (asm_out_file, "L",
+ CODE_LABEL_NUMBER (loperands[6]));
+ break;
+
+ case LTU:
+ (*targetm.asm_out.internal_label) (asm_out_file, "L",
+ CODE_LABEL_NUMBER (loperands[4]));
+ output_asm_insn ("scs %5", loperands);
+ break;
+
+ case GE:
+ loperands[6] = gen_label_rtx();
+#ifdef MOTOROLA
+ output_asm_insn ("scc %5\n\tjbra %l6", loperands);
+#else
+ output_asm_insn ("scc %5\n\tjra %l6", loperands);
+#endif
+ (*targetm.asm_out.internal_label) (asm_out_file, "L",
+ CODE_LABEL_NUMBER (loperands[4]));
+ output_asm_insn ("sge %5", loperands);
+ (*targetm.asm_out.internal_label) (asm_out_file, "L",
+ CODE_LABEL_NUMBER (loperands[6]));
+ break;
+
+ case GEU:
+ (*targetm.asm_out.internal_label) (asm_out_file, "L",
+ CODE_LABEL_NUMBER (loperands[4]));
+ output_asm_insn ("scc %5", loperands);
+ break;
+
+ case LE:
+ loperands[6] = gen_label_rtx();
+#ifdef MOTOROLA
+ output_asm_insn ("sls %5\n\tjbra %l6", loperands);
+#else
+ output_asm_insn ("sls %5\n\tjra %l6", loperands);
+#endif
+ (*targetm.asm_out.internal_label) (asm_out_file, "L",
+ CODE_LABEL_NUMBER (loperands[4]));
+ output_asm_insn ("sle %5", loperands);
+ (*targetm.asm_out.internal_label) (asm_out_file, "L",
+ CODE_LABEL_NUMBER (loperands[6]));
+ break;
+
+ case LEU:
+ (*targetm.asm_out.internal_label) (asm_out_file, "L",
+ CODE_LABEL_NUMBER (loperands[4]));
+ output_asm_insn ("sls %5", loperands);
+ break;
+
+ default:
+ abort ();
+ }
+ return "";
+}
+
+const char *
output_btst (operands, countop, dataop, insn, signpos)
rtx *operands;
rtx countop, dataop;
{
int offset = (count & ~signpos) / 8;
count = count & signpos;
- operands[1] = dataop = adj_offsettable_operand (dataop, offset);
+ operands[1] = dataop = adjust_address (dataop, QImode, offset);
}
if (count == signpos)
cc_status.flags = CC_NOT_POSITIVE | CC_Z_IN_NOT_N;
int
symbolic_operand (op, mode)
register rtx op;
- enum machine_mode mode;
+ enum machine_mode mode ATTRIBUTE_UNUSED;
{
switch (GET_CODE (op))
{
return 0;
}
}
+\f
+/* Check for sign_extend or zero_extend. Used for bit-count operands. */
+
+int
+extend_operator(x, mode)
+ rtx x;
+ enum machine_mode mode;
+{
+ if (mode != VOIDmode && GET_MODE(x) != mode)
+ return 0;
+ switch (GET_CODE(x))
+ {
+ case SIGN_EXTEND :
+ case ZERO_EXTEND :
+ return 1;
+ default :
+ return 0;
+ }
+}
\f
/* Legitimize PIC addresses. If the address is already
rtx
legitimize_pic_address (orig, mode, reg)
rtx orig, reg;
- enum machine_mode mode;
+ enum machine_mode mode ATTRIBUTE_UNUSED;
{
rtx pic_ref = orig;
if (reg == 0)
abort ();
- pic_ref = gen_rtx (MEM, Pmode,
- gen_rtx (PLUS, Pmode,
- pic_offset_table_rtx, orig));
+ pic_ref = gen_rtx_MEM (Pmode,
+ gen_rtx_PLUS (Pmode,
+ pic_offset_table_rtx, orig));
current_function_uses_pic_offset_table = 1;
RTX_UNCHANGING_P (pic_ref) = 1;
emit_move_insn (reg, pic_ref);
}
else if (GET_CODE (orig) == CONST)
{
- rtx base, offset;
+ rtx base;
/* Make sure this is CONST has not already been legitimized */
if (GET_CODE (XEXP (orig, 0)) == PLUS
else abort ();
if (GET_CODE (orig) == CONST_INT)
- return plus_constant_for_output (base, INTVAL (orig));
- pic_ref = gen_rtx (PLUS, Pmode, base, orig);
+ return plus_constant (base, INTVAL (orig));
+ pic_ref = gen_rtx_PLUS (Pmode, base, orig);
/* Likewise, should we set special REG_NOTEs here? */
}
return pic_ref;
}
\f
-/* Return the best assembler insn template
- for moving operands[1] into operands[0] as a fullword. */
+typedef enum { MOVL, SWAP, NEGW, NOTW, NOTB, MOVQ } CONST_METHOD;
-static char *
-singlemove_string (operands)
+static CONST_METHOD const_method PARAMS ((rtx));
+
+#define USE_MOVQ(i) ((unsigned)((i) + 128) <= 255)
+
+static CONST_METHOD
+const_method (constant)
+ rtx constant;
+{
+ int i;
+ unsigned u;
+
+ i = INTVAL (constant);
+ if (USE_MOVQ (i))
+ return MOVQ;
+
+ /* The Coldfire doesn't have byte or word operations. */
+ /* FIXME: This may not be useful for the m68060 either */
+ if (!TARGET_COLDFIRE)
+ {
+ /* if -256 < N < 256 but N is not in range for a moveq
+ N^ff will be, so use moveq #N^ff, dreg; not.b dreg. */
+ if (USE_MOVQ (i ^ 0xff))
+ return NOTB;
+ /* Likewise, try with not.w */
+ if (USE_MOVQ (i ^ 0xffff))
+ return NOTW;
+ /* This is the only value where neg.w is useful */
+ if (i == -65408)
+ return NEGW;
+ /* Try also with swap */
+ u = i;
+ if (USE_MOVQ ((u >> 16) | (u << 16)))
+ return SWAP;
+ }
+ /* Otherwise, use move.l */
+ return MOVL;
+}
+
+static int
+const_int_cost (constant)
+ rtx constant;
+{
+ switch (const_method (constant))
+ {
+ case MOVQ :
+ /* Constants between -128 and 127 are cheap due to moveq */
+ return 0;
+ case NOTB :
+ case NOTW :
+ case NEGW :
+ case SWAP :
+ /* Constants easily generated by moveq + not.b/not.w/neg.w/swap */
+ return 1;
+ case MOVL :
+ return 2;
+ default :
+ abort ();
+ }
+}
+
+static bool
+m68k_rtx_costs (x, code, outer_code, total)
+ rtx x;
+ int code, outer_code;
+ int *total;
+{
+ switch (code)
+ {
+ case CONST_INT:
+ /* Constant zero is super cheap due to clr instruction. */
+ if (x == const0_rtx)
+ *total = 0;
+ else
+ *total = const_int_cost (x);
+ return true;
+
+ case CONST:
+ case LABEL_REF:
+ case SYMBOL_REF:
+ *total = 3;
+ return true;
+
+ case CONST_DOUBLE:
+ /* Make 0.0 cheaper than other floating constants to
+ encourage creating tstsf and tstdf insns. */
+ if (outer_code == COMPARE
+ && (x == CONST0_RTX (SFmode) || x == CONST0_RTX (DFmode)))
+ *total = 4;
+ else
+ *total = 5;
+ return true;
+
+ /* These are vaguely right for a 68020. */
+ /* The costs for long multiply have been adjusted to work properly
+ in synth_mult on the 68020, relative to an average of the time
+ for add and the time for shift, taking away a little more because
+ sometimes move insns are needed. */
+ /* div?.w is relatively cheaper on 68000 counted in COSTS_N_INSNS terms. */
+#define MULL_COST (TARGET_68060 ? 2 : TARGET_68040 ? 5 : TARGET_CFV3 ? 3 : TARGET_COLDFIRE ? 10 : 13)
+#define MULW_COST (TARGET_68060 ? 2 : TARGET_68040 ? 3 : TARGET_68020 ? 8 : \
+ TARGET_CFV3 ? 2 : 5)
+#define DIVW_COST (TARGET_68020 ? 27 : TARGET_CF_HWDIV ? 11 : 12)
+
+ case PLUS:
+ /* An lea costs about three times as much as a simple add. */
+ if (GET_MODE (x) == SImode
+ && GET_CODE (XEXP (x, 1)) == REG
+ && GET_CODE (XEXP (x, 0)) == MULT
+ && GET_CODE (XEXP (XEXP (x, 0), 0)) == REG
+ && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT
+ && (INTVAL (XEXP (XEXP (x, 0), 1)) == 2
+ || INTVAL (XEXP (XEXP (x, 0), 1)) == 4
+ || INTVAL (XEXP (XEXP (x, 0), 1)) == 8))
+ {
+ /* lea an@(dx:l:i),am */
+ *total = COSTS_N_INSNS (TARGET_COLDFIRE ? 2 : 3);
+ return true;
+ }
+ return false;
+
+ case ASHIFT:
+ case ASHIFTRT:
+ case LSHIFTRT:
+ if (TARGET_68060)
+ {
+ *total = COSTS_N_INSNS(1);
+ return true;
+ }
+ if (! TARGET_68020 && ! TARGET_COLDFIRE)
+ {
+ if (GET_CODE (XEXP (x, 1)) == CONST_INT)
+ {
+ if (INTVAL (XEXP (x, 1)) < 16)
+ *total = COSTS_N_INSNS (2) + INTVAL (XEXP (x, 1)) / 2;
+ else
+ /* We're using clrw + swap for these cases. */
+ *total = COSTS_N_INSNS (4) + (INTVAL (XEXP (x, 1)) - 16) / 2;
+ }
+ else
+ *total = COSTS_N_INSNS (10); /* worst case */
+ return true;
+ }
+ /* A shift by a big integer takes an extra instruction. */
+ if (GET_CODE (XEXP (x, 1)) == CONST_INT
+ && (INTVAL (XEXP (x, 1)) == 16))
+ {
+ *total = COSTS_N_INSNS (2); /* clrw;swap */
+ return true;
+ }
+ if (GET_CODE (XEXP (x, 1)) == CONST_INT
+ && !(INTVAL (XEXP (x, 1)) > 0
+ && INTVAL (XEXP (x, 1)) <= 8))
+ {
+ *total = COSTS_N_INSNS (TARGET_COLDFIRE ? 1 : 3); /* lsr #i,dn */
+ return true;
+ }
+ return false;
+
+ case MULT:
+ if ((GET_CODE (XEXP (x, 0)) == ZERO_EXTEND
+ || GET_CODE (XEXP (x, 0)) == SIGN_EXTEND)
+ && GET_MODE (x) == SImode)
+ *total = COSTS_N_INSNS (MULW_COST);
+ else if (GET_MODE (x) == QImode || GET_MODE (x) == HImode)
+ *total = COSTS_N_INSNS (MULW_COST);
+ else
+ *total = COSTS_N_INSNS (MULL_COST);
+ return true;
+
+ case DIV:
+ case UDIV:
+ case MOD:
+ case UMOD:
+ if (GET_MODE (x) == QImode || GET_MODE (x) == HImode)
+ *total = COSTS_N_INSNS (DIVW_COST); /* div.w */
+ else if (TARGET_CF_HWDIV)
+ *total = COSTS_N_INSNS (18);
+ else
+ *total = COSTS_N_INSNS (43); /* div.l */
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+const char *
+output_move_const_into_data_reg (operands)
rtx *operands;
{
-#ifdef SUPPORT_SUN_FPA
- if (FPA_REG_P (operands[0]) || FPA_REG_P (operands[1]))
- return "fpmoves %1,%0";
-#endif
- if (DATA_REG_P (operands[0])
- && GET_CODE (operands[1]) == CONST_INT
+ int i;
+
+ i = INTVAL (operands[1]);
+ switch (const_method (operands[1]))
+ {
+ case MOVQ :
+ return "moveq %1,%0";
+ case NOTB :
+ operands[1] = GEN_INT (i ^ 0xff);
+ return "moveq %1,%0\n\tnot%.b %0";
+ case NOTW :
+ operands[1] = GEN_INT (i ^ 0xffff);
+ return "moveq %1,%0\n\tnot%.w %0";
+ case NEGW :
+ return "moveq %#-128,%0\n\tneg%.w %0";
+ case SWAP :
+ {
+ unsigned u = i;
+
+ operands[1] = GEN_INT ((u << 16) | (u >> 16));
+ return "moveq %1,%0\n\tswap %0";
+ }
+ case MOVL :
+ return "move%.l %1,%0";
+ default :
+ abort ();
+ }
+}
+
+const char *
+output_move_simode_const (operands)
+ rtx *operands;
+{
+ if (operands[1] == const0_rtx
+ && (DATA_REG_P (operands[0])
+ || GET_CODE (operands[0]) == MEM)
+ /* clr insns on 68000 read before writing.
+ This isn't so on the 68010, but we have no TARGET_68010. */
+ && ((TARGET_68020 || TARGET_COLDFIRE)
+ || !(GET_CODE (operands[0]) == MEM
+ && MEM_VOLATILE_P (operands[0]))))
+ return "clr%.l %0";
+ else if (operands[1] == const0_rtx
+ && ADDRESS_REG_P (operands[0]))
+ return "sub%.l %0,%0";
+ else if (DATA_REG_P (operands[0]))
+ return output_move_const_into_data_reg (operands);
+ else if (ADDRESS_REG_P (operands[0])
+ && INTVAL (operands[1]) < 0x8000
+ && INTVAL (operands[1]) >= -0x8000)
+ return "move%.w %1,%0";
+ else if (GET_CODE (operands[0]) == MEM
+ && GET_CODE (XEXP (operands[0], 0)) == PRE_DEC
+ && REGNO (XEXP (XEXP (operands[0], 0), 0)) == STACK_POINTER_REGNUM
+ && INTVAL (operands[1]) < 0x8000
+ && INTVAL (operands[1]) >= -0x8000)
+ return "pea %a1";
+ return "move%.l %1,%0";
+}
+
+const char *
+output_move_simode (operands)
+ rtx *operands;
+{
+ if (GET_CODE (operands[1]) == CONST_INT)
+ return output_move_simode_const (operands);
+ else if ((GET_CODE (operands[1]) == SYMBOL_REF
+ || GET_CODE (operands[1]) == CONST)
+ && push_operand (operands[0], SImode))
+ return "pea %a1";
+ else if ((GET_CODE (operands[1]) == SYMBOL_REF
+ || GET_CODE (operands[1]) == CONST)
+ && ADDRESS_REG_P (operands[0]))
+ return "lea %a1,%0";
+ return "move%.l %1,%0";
+}
+
+const char *
+output_move_himode (operands)
+ rtx *operands;
+{
+ if (GET_CODE (operands[1]) == CONST_INT)
+ {
+ if (operands[1] == const0_rtx
+ && (DATA_REG_P (operands[0])
+ || GET_CODE (operands[0]) == MEM)
+ /* clr insns on 68000 read before writing.
+ This isn't so on the 68010, but we have no TARGET_68010. */
+ && ((TARGET_68020 || TARGET_COLDFIRE)
+ || !(GET_CODE (operands[0]) == MEM
+ && MEM_VOLATILE_P (operands[0]))))
+ return "clr%.w %0";
+ else if (operands[1] == const0_rtx
+ && ADDRESS_REG_P (operands[0]))
+ return "sub%.l %0,%0";
+ else if (DATA_REG_P (operands[0])
+ && INTVAL (operands[1]) < 128
+ && INTVAL (operands[1]) >= -128)
+ {
+ return "moveq %1,%0";
+ }
+ else if (INTVAL (operands[1]) < 0x8000
+ && INTVAL (operands[1]) >= -0x8000)
+ return "move%.w %1,%0";
+ }
+ else if (CONSTANT_P (operands[1]))
+ return "move%.l %1,%0";
+ /* Recognize the insn before a tablejump, one that refers
+ to a table of offsets. Such an insn will need to refer
+ to a label on the insn. So output one. Use the label-number
+ of the table of offsets to generate this label. This code,
+ and similar code below, assumes that there will be at most one
+ reference to each table. */
+ if (GET_CODE (operands[1]) == MEM
+ && GET_CODE (XEXP (operands[1], 0)) == PLUS
+ && GET_CODE (XEXP (XEXP (operands[1], 0), 1)) == LABEL_REF
+ && GET_CODE (XEXP (XEXP (operands[1], 0), 0)) != PLUS)
+ {
+ rtx labelref = XEXP (XEXP (operands[1], 0), 1);
+#if defined (MOTOROLA) && !defined (SGS_SWITCH_TABLES)
+#ifdef SGS
+ asm_fprintf (asm_out_file, "\tset %LLI%d,.+2\n",
+ CODE_LABEL_NUMBER (XEXP (labelref, 0)));
+#else /* not SGS */
+ asm_fprintf (asm_out_file, "\t.set %LLI%d,.+2\n",
+ CODE_LABEL_NUMBER (XEXP (labelref, 0)));
+#endif /* not SGS */
+#else /* SGS_SWITCH_TABLES or not MOTOROLA */
+ (*targetm.asm_out.internal_label) (asm_out_file, "LI",
+ CODE_LABEL_NUMBER (XEXP (labelref, 0)));
+#ifdef SGS_SWITCH_TABLES
+ /* Set flag saying we need to define the symbol
+ LD%n (with value L%n-LI%n) at the end of the switch table. */
+ switch_table_difference_label_flag = 1;
+#endif /* SGS_SWITCH_TABLES */
+#endif /* SGS_SWITCH_TABLES or not MOTOROLA */
+ }
+ return "move%.w %1,%0";
+}
+
+const char *
+output_move_qimode (operands)
+ rtx *operands;
+{
+ rtx xoperands[4];
+
+ /* This is probably useless, since it loses for pushing a struct
+ of several bytes a byte at a time. */
+ /* 68k family always modifies the stack pointer by at least 2, even for
+ byte pushes. The 5200 (coldfire) does not do this. */
+ if (GET_CODE (operands[0]) == MEM
+ && GET_CODE (XEXP (operands[0], 0)) == PRE_DEC
+ && XEXP (XEXP (operands[0], 0), 0) == stack_pointer_rtx
+ && ! ADDRESS_REG_P (operands[1])
+ && ! TARGET_COLDFIRE)
+ {
+ xoperands[1] = operands[1];
+ xoperands[2]
+ = gen_rtx_MEM (QImode,
+ gen_rtx_PLUS (VOIDmode, stack_pointer_rtx, const1_rtx));
+ /* Just pushing a byte puts it in the high byte of the halfword. */
+ /* We must put it in the low-order, high-numbered byte. */
+ if (!reg_mentioned_p (stack_pointer_rtx, operands[1]))
+ {
+ xoperands[3] = stack_pointer_rtx;
+ output_asm_insn ("subq%.l %#2,%3\n\tmove%.b %1,%2", xoperands);
+ }
+ else
+ output_asm_insn ("move%.b %1,%-\n\tmove%.b %@,%2", xoperands);
+ return "";
+ }
+
+ /* clr and st insns on 68000 read before writing.
+ This isn't so on the 68010, but we have no TARGET_68010. */
+ if (!ADDRESS_REG_P (operands[0])
+ && ((TARGET_68020 || TARGET_COLDFIRE)
+ || !(GET_CODE (operands[0]) == MEM && MEM_VOLATILE_P (operands[0]))))
+ {
+ if (operands[1] == const0_rtx)
+ return "clr%.b %0";
+ if ((!TARGET_COLDFIRE || DATA_REG_P (operands[0]))
+ && GET_CODE (operands[1]) == CONST_INT
+ && (INTVAL (operands[1]) & 255) == 255)
+ {
+ CC_STATUS_INIT;
+ return "st %0";
+ }
+ }
+ if (GET_CODE (operands[1]) == CONST_INT
+ && DATA_REG_P (operands[0])
&& INTVAL (operands[1]) < 128
&& INTVAL (operands[1]) >= -128)
{
-#if defined (MOTOROLA) && !defined (CRDS)
- return "moveq%.l %1,%0";
-#else
return "moveq %1,%0";
-#endif
}
- if (operands[1] != const0_rtx)
+ if (operands[1] == const0_rtx && ADDRESS_REG_P (operands[0]))
+ return "sub%.l %0,%0";
+ if (GET_CODE (operands[1]) != CONST_INT && CONSTANT_P (operands[1]))
return "move%.l %1,%0";
- if (! ADDRESS_REG_P (operands[0]))
- return "clr%.l %0";
- return "sub%.l %0,%0";
+ /* 68k family (including the 5200 coldfire) does not support byte moves to
+ from address registers. */
+ if (ADDRESS_REG_P (operands[0]) || ADDRESS_REG_P (operands[1]))
+ return "move%.w %1,%0";
+ return "move%.b %1,%0";
+}
+
+const char *
+output_move_stricthi (operands)
+ rtx *operands;
+{
+ if (operands[1] == const0_rtx
+ /* clr insns on 68000 read before writing.
+ This isn't so on the 68010, but we have no TARGET_68010. */
+ && ((TARGET_68020 || TARGET_COLDFIRE)
+ || !(GET_CODE (operands[0]) == MEM && MEM_VOLATILE_P (operands[0]))))
+ return "clr%.w %0";
+ return "move%.w %1,%0";
+}
+
+const char *
+output_move_strictqi (operands)
+ rtx *operands;
+{
+ if (operands[1] == const0_rtx
+ /* clr insns on 68000 read before writing.
+ This isn't so on the 68010, but we have no TARGET_68010. */
+ && ((TARGET_68020 || TARGET_COLDFIRE)
+ || !(GET_CODE (operands[0]) == MEM && MEM_VOLATILE_P (operands[0]))))
+ return "clr%.b %0";
+ return "move%.b %1,%0";
+}
+
+/* Return the best assembler insn template
+ for moving operands[1] into operands[0] as a fullword. */
+
+static const char *
+singlemove_string (operands)
+ rtx *operands;
+{
+ if (GET_CODE (operands[1]) == CONST_INT)
+ return output_move_simode_const (operands);
+ return "move%.l %1,%0";
}
/* Output assembler code to perform a doubleword move insn
with operands OPERANDS. */
-char *
+const char *
output_move_double (operands)
rtx *operands;
{
else
output_asm_insn ("subq%.l %#8,%0", operands);
if (GET_MODE (operands[1]) == XFmode)
- operands[0] = gen_rtx (MEM, XFmode, operands[0]);
+ operands[0] = gen_rtx_MEM (XFmode, operands[0]);
else if (GET_MODE (operands[0]) == DFmode)
- operands[0] = gen_rtx (MEM, DFmode, operands[0]);
+ operands[0] = gen_rtx_MEM (DFmode, operands[0]);
else
- operands[0] = gen_rtx (MEM, DImode, operands[0]);
+ operands[0] = gen_rtx_MEM (DImode, operands[0]);
optype0 = OFFSOP;
}
if (optype0 == POPOP && optype1 == PUSHOP)
else
output_asm_insn ("subq%.l %#8,%1", operands);
if (GET_MODE (operands[1]) == XFmode)
- operands[1] = gen_rtx (MEM, XFmode, operands[1]);
+ operands[1] = gen_rtx_MEM (XFmode, operands[1]);
else if (GET_MODE (operands[1]) == DFmode)
- operands[1] = gen_rtx (MEM, DFmode, operands[1]);
+ operands[1] = gen_rtx_MEM (DFmode, operands[1]);
else
- operands[1] = gen_rtx (MEM, DImode, operands[1]);
+ operands[1] = gen_rtx_MEM (DImode, operands[1]);
optype1 = OFFSOP;
}
{
if (optype0 == REGOP)
{
- latehalf[0] = gen_rtx (REG, SImode, REGNO (operands[0]) + 2);
- middlehalf[0] = gen_rtx (REG, SImode, REGNO (operands[0]) + 1);
+ latehalf[0] = gen_rtx_REG (SImode, REGNO (operands[0]) + 2);
+ middlehalf[0] = gen_rtx_REG (SImode, REGNO (operands[0]) + 1);
}
else if (optype0 == OFFSOP)
{
- middlehalf[0] = adj_offsettable_operand (operands[0], 4);
- latehalf[0] = adj_offsettable_operand (operands[0], size - 4);
+ middlehalf[0] = adjust_address (operands[0], SImode, 4);
+ latehalf[0] = adjust_address (operands[0], SImode, size - 4);
}
else
{
if (optype1 == REGOP)
{
- latehalf[1] = gen_rtx (REG, SImode, REGNO (operands[1]) + 2);
- middlehalf[1] = gen_rtx (REG, SImode, REGNO (operands[1]) + 1);
+ latehalf[1] = gen_rtx_REG (SImode, REGNO (operands[1]) + 2);
+ middlehalf[1] = gen_rtx_REG (SImode, REGNO (operands[1]) + 1);
}
else if (optype1 == OFFSOP)
{
- middlehalf[1] = adj_offsettable_operand (operands[1], 4);
- latehalf[1] = adj_offsettable_operand (operands[1], size - 4);
+ middlehalf[1] = adjust_address (operands[1], SImode, 4);
+ latehalf[1] = adjust_address (operands[1], SImode, size - 4);
}
else if (optype1 == CNSTOP)
{
/* size is not 12: */
{
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], size - 4);
+ latehalf[0] = adjust_address (operands[0], SImode, size - 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], size - 4);
+ latehalf[1] = adjust_address (operands[1], SImode, size - 4);
else if (optype1 == CNSTOP)
split_double (operands[1], &operands[1], &latehalf[1]);
else
if (optype0 == REGOP
&& (optype1 == OFFSOP || optype1 == MEMOP))
{
- rtx testlow = gen_rtx (REG, SImode, REGNO (operands[0]));
+ rtx testlow = gen_rtx_REG (SImode, REGNO (operands[0]));
if (reg_overlap_mentioned_p (testlow, XEXP (operands[1], 0))
&& reg_overlap_mentioned_p (latehalf[0], XEXP (operands[1], 0)))
xops[0] = latehalf[0];
xops[1] = XEXP (operands[1], 0);
output_asm_insn ("lea %a1,%0", xops);
- if( GET_MODE (operands[1]) == XFmode )
+ if (GET_MODE (operands[1]) == XFmode )
{
- operands[1] = gen_rtx (MEM, XFmode, latehalf[0]);
- middlehalf[1] = adj_offsettable_operand (operands[1], size-8);
- latehalf[1] = adj_offsettable_operand (operands[1], size-4);
+ operands[1] = gen_rtx_MEM (XFmode, latehalf[0]);
+ middlehalf[1] = adjust_address (operands[1], DImode, size - 8);
+ latehalf[1] = adjust_address (operands[1], DImode, size - 4);
}
else
{
- operands[1] = gen_rtx (MEM, DImode, latehalf[0]);
- latehalf[1] = adj_offsettable_operand (operands[1], size-4);
+ operands[1] = gen_rtx_MEM (DImode, latehalf[0]);
+ latehalf[1] = adjust_address (operands[1], DImode, size - 4);
}
}
else if (size == 12
if (addreg0 || addreg1)
abort ();
- /* Only the middle reg conflicts; simply put it last. */
+ /* Only the middle reg conflicts; simply put it last. */
output_asm_insn (singlemove_string (operands), operands);
output_asm_insn (singlemove_string (latehalf), latehalf);
output_asm_insn (singlemove_string (middlehalf), middlehalf);
else
abort ();
}
- if (GET_CODE (addr) == REG)
- return addr;
- abort ();
+ if (GET_CODE (addr) == REG)
+ return addr;
+ abort ();
+}
+
+/* Output assembler code to perform a 32 bit 3 operand add. */
+
+const char *
+output_addsi3 (operands)
+ rtx *operands;
+{
+ if (! operands_match_p (operands[0], operands[1]))
+ {
+ if (!ADDRESS_REG_P (operands[1]))
+ {
+ rtx tmp = operands[1];
+
+ operands[1] = operands[2];
+ operands[2] = tmp;
+ }
+
+ /* These insns can result from reloads to access
+ stack slots over 64k from the frame pointer. */
+ if (GET_CODE (operands[2]) == CONST_INT
+ && INTVAL (operands[2]) + 0x8000 >= (unsigned) 0x10000)
+ return "move%.l %2,%0\n\tadd%.l %1,%0";
+#ifdef SGS
+ if (GET_CODE (operands[2]) == REG)
+ return "lea 0(%1,%2.l),%0";
+ else
+ return "lea %c2(%1),%0";
+#else /* not SGS */
+#ifdef MOTOROLA
+ if (GET_CODE (operands[2]) == REG)
+ return "lea (%1,%2.l),%0";
+ else
+ return "lea (%c2,%1),%0";
+#else /* not MOTOROLA (MIT syntax) */
+ if (GET_CODE (operands[2]) == REG)
+ return "lea %1@(0,%2:l),%0";
+ else
+ return "lea %1@(%c2),%0";
+#endif /* not MOTOROLA */
+#endif /* not SGS */
+ }
+ if (GET_CODE (operands[2]) == CONST_INT)
+ {
+ if (INTVAL (operands[2]) > 0
+ && INTVAL (operands[2]) <= 8)
+ return "addq%.l %2,%0";
+ if (INTVAL (operands[2]) < 0
+ && INTVAL (operands[2]) >= -8)
+ {
+ operands[2] = GEN_INT (- INTVAL (operands[2]));
+ return "subq%.l %2,%0";
+ }
+ /* On the CPU32 it is faster to use two addql instructions to
+ add a small integer (8 < N <= 16) to a register.
+ Likewise for subql. */
+ if (TARGET_CPU32 && REG_P (operands[0]))
+ {
+ if (INTVAL (operands[2]) > 8
+ && INTVAL (operands[2]) <= 16)
+ {
+ operands[2] = GEN_INT (INTVAL (operands[2]) - 8);
+ return "addq%.l %#8,%0\n\taddq%.l %2,%0";
+ }
+ if (INTVAL (operands[2]) < -8
+ && INTVAL (operands[2]) >= -16)
+ {
+ operands[2] = GEN_INT (- INTVAL (operands[2]) - 8);
+ return "subq%.l %#8,%0\n\tsubq%.l %2,%0";
+ }
+ }
+ if (ADDRESS_REG_P (operands[0])
+ && INTVAL (operands[2]) >= -0x8000
+ && INTVAL (operands[2]) < 0x8000)
+ {
+ if (TARGET_68040)
+ return "add%.w %2,%0";
+ else
+#ifdef MOTOROLA
+ return "lea (%c2,%0),%0";
+#else
+ return "lea %0@(%c2),%0";
+#endif
+ }
+ }
+ return "add%.l %2,%0";
}
\f
/* Store in cc_status the expressions that the condition codes will
possibly invalid to use the saved cc's. In those cases we clear out
some or all of the saved cc's so they won't be used. */
+void
notice_update_cc (exp, insn)
rtx exp;
rtx insn;
{
- /* If the cc is being set from the fpa and the expression is not an
- explicit floating point test instruction (which has code to deal with
- this), reinit the CC. */
- if (((cc_status.value1 && FPA_REG_P (cc_status.value1))
- || (cc_status.value2 && FPA_REG_P (cc_status.value2)))
- && !(GET_CODE (exp) == PARALLEL
- && GET_CODE (XVECEXP (exp, 0, 0)) == SET
- && XEXP (XVECEXP (exp, 0, 0), 0) == cc0_rtx))
- {
- CC_STATUS_INIT;
- }
- else if (GET_CODE (exp) == SET)
+ if (GET_CODE (exp) == SET)
{
if (GET_CODE (SET_SRC (exp)) == CALL)
{
}
else if (ADDRESS_REG_P (SET_DEST (exp)))
{
- if (cc_status.value1
- && reg_overlap_mentioned_p (SET_DEST (exp), cc_status.value1))
+ if (cc_status.value1 && modified_in_p (cc_status.value1, insn))
cc_status.value1 = 0;
- if (cc_status.value2
- && reg_overlap_mentioned_p (SET_DEST (exp), cc_status.value2))
+ if (cc_status.value2 && modified_in_p (cc_status.value2, insn))
cc_status.value2 = 0;
}
else if (!FP_REG_P (SET_DEST (exp))
&& ADDRESS_REG_P (cc_status.value2)
&& GET_MODE (cc_status.value2) == QImode)
CC_STATUS_INIT;
- if (cc_status.value2 != 0
- && !(cc_status.value1 && FPA_REG_P (cc_status.value1)))
+ if (cc_status.value2 != 0)
switch (GET_CODE (cc_status.value2))
{
case PLUS: case MINUS: case MULT:
case DIV: case UDIV: case MOD: case UMOD: case NEG:
+#if 0 /* These instructions always clear the overflow bit */
case ASHIFT: case ASHIFTRT: case LSHIFTRT:
case ROTATE: case ROTATERT:
+#endif
if (GET_MODE (cc_status.value2) != VOIDmode)
cc_status.flags |= CC_NO_OVERFLOW;
break;
/* (SET r1 (ZERO_EXTEND r2)) on this machine
ends with a move insn moving r2 in r2's mode.
Thus, the cc's are set for r2.
- This can set N bit spuriously. */
+ This can set N bit spuriously. */
cc_status.flags |= CC_NOT_NEGATIVE;
+
+ default:
+ break;
}
if (cc_status.value1 && GET_CODE (cc_status.value1) == REG
&& cc_status.value2
&& reg_overlap_mentioned_p (cc_status.value1, cc_status.value2))
cc_status.value2 = 0;
if (((cc_status.value1 && FP_REG_P (cc_status.value1))
- || (cc_status.value2 && FP_REG_P (cc_status.value2)))
- && !((cc_status.value1 && FPA_REG_P (cc_status.value1))
- || (cc_status.value2 && FPA_REG_P (cc_status.value2))))
+ || (cc_status.value2 && FP_REG_P (cc_status.value2))))
cc_status.flags = CC_IN_68881;
}
\f
-char *
+const char *
output_move_const_double (operands)
rtx *operands;
{
-#ifdef SUPPORT_SUN_FPA
- if (TARGET_FPA && FPA_REG_P (operands[0]))
- {
- int code = standard_sun_fpa_constant_p (operands[1]);
+ int code = standard_68881_constant_p (operands[1]);
- if (code != 0)
- {
- static char buf[40];
-
- sprintf (buf, "fpmove%%.d %%%%%d,%%0", code & 0x1ff);
- return buf;
- }
- return "fpmove%.d %1,%0";
- }
- else
-#endif
+ if (code != 0)
{
- int code = standard_68881_constant_p (operands[1]);
-
- if (code != 0)
- {
- static char buf[40];
+ static char buf[40];
- sprintf (buf, "fmovecr %%#0x%x,%%0", code & 0xff);
- return buf;
- }
- return "fmove%.d %1,%0";
+ sprintf (buf, "fmovecr %%#0x%x,%%0", code & 0xff);
+ return buf;
}
+ return "fmove%.d %1,%0";
}
-char *
+const char *
output_move_const_single (operands)
rtx *operands;
{
-#ifdef SUPPORT_SUN_FPA
- if (TARGET_FPA)
- {
- int code = standard_sun_fpa_constant_p (operands[1]);
-
- if (code != 0)
- {
- static char buf[40];
+ int code = standard_68881_constant_p (operands[1]);
- sprintf (buf, "fpmove%%.s %%%%%d,%%0", code & 0x1ff);
- return buf;
- }
- return "fpmove%.s %1,%0";
- }
- else
-#endif /* defined SUPPORT_SUN_FPA */
+ if (code != 0)
{
- int code = standard_68881_constant_p (operands[1]);
-
- if (code != 0)
- {
- static char buf[40];
+ static char buf[40];
- sprintf (buf, "fmovecr %%#0x%x,%%0", code & 0xff);
- return buf;
- }
- return "fmove%.s %f1,%0";
+ sprintf (buf, "fmovecr %%#0x%x,%%0", code & 0xff);
+ return buf;
}
+ return "fmove%.s %f1,%0";
}
/* Return nonzero if X, a CONST_DOUBLE, has a value that we can get
The value, anded with 0xff, gives the code to use in fmovecr
to get the desired constant. */
-/* This code has been fixed for cross-compilation. */
+/* This code has been fixed for cross-compilation. */
static int inited_68881_table = 0;
-char *strings_68881[7] = {
+static const char *const strings_68881[7] = {
"0.0",
"1.0",
"10.0",
"1e16"
};
-int codes_68881[7] = {
+static const int codes_68881[7] = {
0x0f,
0x32,
0x33,
REAL_VALUE_TYPE values_68881[7];
/* Set up values_68881 array by converting the decimal values
- strings_68881 to binary. */
+ strings_68881 to binary. */
void
init_68881_table ()
REAL_VALUE_TYPE r;
enum machine_mode mode;
- mode = DFmode;
+ mode = SFmode;
for (i = 0; i < 7; i++)
{
if (i == 6)
- mode = SFmode;
+ mode = DFmode;
r = REAL_VALUE_ATOF (strings_68881[i], mode);
values_68881[i] = r;
}
{
REAL_VALUE_TYPE r;
int i;
- enum machine_mode mode;
-
-#ifdef NO_ASM_FMOVECR
- return 0;
-#endif
-
- /* fmovecr must be emulated on the 68040, so it shouldn't be used at all. */
- if (TARGET_68040)
- return 0;
-#ifndef REAL_ARITHMETIC
-#if HOST_FLOAT_FORMAT != TARGET_FLOAT_FORMAT
- if (! flag_pretend_float)
+ /* fmovecr must be emulated on the 68040 and 68060, so it shouldn't be
+ used at all on those chips. */
+ if (TARGET_68040 || TARGET_68060)
return 0;
-#endif
-#endif
if (! inited_68881_table)
init_68881_table ();
REAL_VALUE_FROM_CONST_DOUBLE (r, x);
+ /* Use REAL_VALUES_IDENTICAL instead of REAL_VALUES_EQUAL so that -0.0
+ is rejected. */
for (i = 0; i < 6; i++)
{
- if (REAL_VALUES_EQUAL (r, values_68881[i]))
+ if (REAL_VALUES_IDENTICAL (r, values_68881[i]))
return (codes_68881[i]);
}
rtx x;
{
REAL_VALUE_TYPE r, r1;
- int i;
-
-#ifndef REAL_ARITHMETIC
-#if HOST_FLOAT_FORMAT != TARGET_FLOAT_FORMAT
- if (! flag_pretend_float)
- return 0;
-#endif
-#endif
+ int exp;
REAL_VALUE_FROM_CONST_DOUBLE (r, x);
- if (REAL_VALUES_LESS (r, dconst0))
- return 0;
-
- r1 = dconst1;
- i = 0;
- while (REAL_VALUES_LESS (r1, r))
- {
- r1 = REAL_VALUE_LDEXP (dconst1, i);
- if (REAL_VALUES_EQUAL (r1, r))
- return i;
- i = i + 1;
- }
- return 0;
-}
-\f
-#ifdef SUPPORT_SUN_FPA
-/* Return nonzero if X, a CONST_DOUBLE, has a value that we can get
- from the Sun FPA's constant RAM.
- The value returned, anded with 0x1ff, gives the code to use in fpmove
- to get the desired constant. */
-
-static int inited_FPA_table = 0;
-
-char *strings_FPA[38] = {
-/* small rationals */
- "0.0",
- "1.0",
- "0.5",
- "-1.0",
- "2.0",
- "3.0",
- "4.0",
- "8.0",
- "0.25",
- "0.125",
- "10.0",
- "-0.5",
-/* Decimal equivalents of double precision values */
- "2.718281828459045091", /* D_E */
- "6.283185307179586477", /* 2 pi */
- "3.141592653589793116", /* D_PI */
- "1.570796326794896619", /* pi/2 */
- "1.414213562373095145", /* D_SQRT2 */
- "0.7071067811865475244", /* 1/sqrt(2) */
- "-1.570796326794896619", /* -pi/2 */
- "1.442695040888963387", /* D_LOG2ofE */
- "3.321928024887362182", /* D_LOG2of10 */
- "0.6931471805599452862", /* D_LOGEof2 */
- "2.302585092994045901", /* D_LOGEof10 */
- "0.3010299956639811980", /* D_LOG10of2 */
- "0.4342944819032518167", /* D_LOG10ofE */
-/* Decimal equivalents of single precision values */
- "2.718281745910644531", /* S_E */
- "6.283185307179586477", /* 2 pi */
- "3.141592741012573242", /* S_PI */
- "1.570796326794896619", /* pi/2 */
- "1.414213538169860840", /* S_SQRT2 */
- "0.7071067811865475244", /* 1/sqrt(2) */
- "-1.570796326794896619", /* -pi/2 */
- "1.442695021629333496", /* S_LOG2ofE */
- "3.321928024291992188", /* S_LOG2of10 */
- "0.6931471824645996094", /* S_LOGEof2 */
- "2.302585124969482442", /* S_LOGEof10 */
- "0.3010300099849700928", /* S_LOG10of2 */
- "0.4342944920063018799", /* S_LOG10ofE */
-};
-
-
-int codes_FPA[38] = {
-/* small rationals */
- 0x200,
- 0xe,
- 0xf,
- 0x10,
- 0x11,
- 0xb1,
- 0x12,
- 0x13,
- 0x15,
- 0x16,
- 0x17,
- 0x2e,
-/* double precision */
- 0x8,
- 0x9,
- 0xa,
- 0xb,
- 0xc,
- 0xd,
- 0x27,
- 0x28,
- 0x29,
- 0x2a,
- 0x2b,
- 0x2c,
- 0x2d,
-/* single precision */
- 0x8,
- 0x9,
- 0xa,
- 0xb,
- 0xc,
- 0xd,
- 0x27,
- 0x28,
- 0x29,
- 0x2a,
- 0x2b,
- 0x2c,
- 0x2d
- };
-
-REAL_VALUE_TYPE values_FPA[38];
-
-/* This code has been fixed for cross-compilation. */
-
-void
-init_FPA_table ()
-{
- enum machine_mode mode;
- int i;
- REAL_VALUE_TYPE r;
-
- mode = DFmode;
- for (i = 0; i < 38; i++)
- {
- if (i == 25)
- mode = SFmode;
- r = REAL_VALUE_ATOF (strings_FPA[i], mode);
- values_FPA[i] = r;
- }
- inited_FPA_table = 1;
-}
-
-
-int
-standard_sun_fpa_constant_p (x)
- rtx x;
-{
- REAL_VALUE_TYPE r;
- int i;
-
-#ifndef REAL_ARITHMETIC
-#if HOST_FLOAT_FORMAT != TARGET_FLOAT_FORMAT
- if (! flag_pretend_float)
+ if (REAL_VALUES_LESS (r, dconst1))
return 0;
-#endif
-#endif
-
- if (! inited_FPA_table)
- init_FPA_table ();
- REAL_VALUE_FROM_CONST_DOUBLE (r, x);
-
- for (i=0; i<12; i++)
- {
- if (REAL_VALUES_EQUAL (r, values_FPA[i]))
- return (codes_FPA[i]);
- }
+ exp = real_exponent (&r);
+ real_2expN (&r1, exp);
+ if (REAL_VALUES_EQUAL (r1, r))
+ return exp;
- if (GET_MODE (x) == SFmode)
- {
- for (i=25; i<38; i++)
- {
- if (REAL_VALUES_EQUAL (r, values_FPA[i]))
- return (codes_FPA[i]);
- }
- }
- else
- {
- for (i=12; i<25; i++)
- {
- if (REAL_VALUES_EQUAL (r, values_FPA[i]))
- return (codes_FPA[i]);
- }
- }
- return 0x0;
+ return 0;
}
-#endif /* define SUPPORT_SUN_FPA */
\f
/* A C compound statement to output to stdio stream STREAM the
assembler syntax for an instruction operand X. X is an RTL
'b' for byte insn (no effect, on the Sun; this is for the ISI).
'd' to force memory addressing to be absolute, not relative.
'f' for float insn (print a CONST_DOUBLE as a float rather than in hex)
- 'w' for FPA insn (print a CONST_DOUBLE as a SunFPA constant rather
- than directly). Second part of 'y' below.
+ 'o' for operands to go directly to output_operand_address (bypassing
+ print_operand_address--used only for SYMBOL_REFs under TARGET_PCREL)
'x' for float insn (print a CONST_DOUBLE as a float rather than in hex),
or print pair of registers as rx:ry.
- 'y' for a FPA insn (print pair of registers as rx:ry). This also outputs
- CONST_DOUBLE's as SunFPA constant RAM registers if
- possible, so it should not be used except for the SunFPA.
*/
rtx op; /* operand to print */
int letter; /* %<letter> or 0 */
{
- int i;
-
if (letter == '.')
{
-#ifdef MOTOROLA
- asm_fprintf (file, ".");
+#if defined (MOTOROLA)
+ fprintf (file, ".");
#endif
}
else if (letter == '#')
{
- asm_fprintf (file, "%0I");
+ asm_fprintf (file, "%I");
}
else if (letter == '-')
{
{
asm_fprintf (file, "%R");
}
+ else if (letter == 'o')
+ {
+ /* This is only for direct addresses with TARGET_PCREL */
+ if (GET_CODE (op) != MEM || GET_CODE (XEXP (op, 0)) != SYMBOL_REF
+ || !TARGET_PCREL)
+ abort ();
+ output_addr_const (file, XEXP (op, 0));
+ }
else if (GET_CODE (op) == REG)
{
-#ifdef SUPPORT_SUN_FPA
- if (REGNO (op) < 16
- && (letter == 'y' || letter == 'x')
- && GET_MODE (op) == DFmode)
- {
- fprintf (file, "%s:%s", reg_names[REGNO (op)],
- reg_names[REGNO (op)+1]);
- }
+ if (letter == 'R')
+ /* Print out the second register name of a register pair.
+ I.e., R (6) => 7. */
+ fputs (reg_names[REGNO (op) + 1], file);
else
-#endif
- {
- fprintf (file, "%s", reg_names[REGNO (op)]);
- }
+ fputs (reg_names[REGNO (op)], file);
}
else if (GET_CODE (op) == MEM)
{
&& INTVAL (XEXP (op, 0)) < 0x8000
&& INTVAL (XEXP (op, 0)) >= -0x8000))
{
+#ifdef MOTOROLA
+ fprintf (file, ".l");
+#else
fprintf (file, ":l");
+#endif
}
}
-#ifdef SUPPORT_SUN_FPA
- else if ((letter == 'y' || letter == 'w')
- && GET_CODE (op) == CONST_DOUBLE
- && (i = standard_sun_fpa_constant_p (op)))
- {
- fprintf (file, "%%%d", i & 0x1ff);
- }
-#endif
else if (GET_CODE (op) == CONST_DOUBLE && GET_MODE (op) == SFmode)
{
REAL_VALUE_TYPE r;
}
else
{
- asm_fprintf (file, "%0I"); output_addr_const (file, op);
+ /* Use `print_operand_address' instead of `output_addr_const'
+ to ensure that we print relevant PIC stuff. */
+ asm_fprintf (file, "%I");
+ if (TARGET_PCREL
+ && (GET_CODE (op) == SYMBOL_REF || GET_CODE (op) == CONST))
+ print_operand_address (file, op);
+ else
+ output_addr_const (file, op);
}
}
macro. See m68k/sgs.h for an example; for versions without the bug.
Some assemblers refuse all the above solutions. The workaround is to
emit "K(pc,d0.l*2)" with K being a small constant known to give the
- right behaviour.
+ right behavior.
They also do not like things like "pea 1.w", so we simple leave off
the .w on small constants.
{
output_addr_const (file, addr);
if (flag_pic && (breg == pic_offset_table_rtx))
- fprintf (file, "@GOT");
+ {
+ fprintf (file, "@GOT");
+ if (flag_pic == 1)
+ fprintf (file, ".w");
+ }
}
fprintf (file, "(%s", reg_names[REGNO (breg)]);
if (ireg != 0)
fprintf (file, "l)");
break;
}
- /* FALL-THROUGH (is this really what we want? */
+ /* FALL-THROUGH (is this really what we want?) */
default:
if (GET_CODE (addr) == CONST_INT
&& INTVAL (addr) < 0x8000
{
#ifdef MOTOROLA
#ifdef SGS
- /* Many SGS assemblers croak on size specifiers for constants. */
- fprintf (file, "%d", INTVAL (addr));
+ /* Many SGS assemblers croak on size specifiers for constants. */
+ fprintf (file, "%d", (int) INTVAL (addr));
#else
- fprintf (file, "%d.w", INTVAL (addr));
+ fprintf (file, "%d.w", (int) INTVAL (addr));
#endif
#else
- fprintf (file, "%d:w", INTVAL (addr));
+ fprintf (file, "%d:w", (int) INTVAL (addr));
#endif
}
- else
+ else if (GET_CODE (addr) == CONST_INT)
+ {
+ fprintf (file, HOST_WIDE_INT_PRINT_DEC, INTVAL (addr));
+ }
+ else if (TARGET_PCREL)
{
+ fputc ('(', file);
output_addr_const (file, addr);
+ if (flag_pic == 1)
+ asm_fprintf (file, ":w,%Rpc)");
+ else
+ asm_fprintf (file, ":l,%Rpc)");
+ }
+ else
+ {
+ /* Special case for SYMBOL_REF if the symbol name ends in
+ `.<letter>', this can be mistaken as a size suffix. Put
+ the name in parentheses. */
+ if (GET_CODE (addr) == SYMBOL_REF
+ && strlen (XSTR (addr, 0)) > 2
+ && XSTR (addr, 0)[strlen (XSTR (addr, 0)) - 2] == '.')
+ {
+ putc ('(', file);
+ output_addr_const (file, addr);
+ putc (')', file);
+ }
+ else
+ output_addr_const (file, addr);
}
break;
}
return 0;
}
+
+/* Accept integer operands in the range 0..0xffffffff. We have to check the
+ range carefully since this predicate is used in DImode contexts. Also, we
+ need some extra crud to make it work when hosted on 64-bit machines. */
+
+int
+const_uint32_operand (op, mode)
+ rtx op;
+ enum machine_mode mode;
+{
+ /* It doesn't make sense to ask this question with a mode that is
+ not larger than 32 bits. */
+ if (GET_MODE_BITSIZE (mode) <= 32)
+ abort ();
+
+#if HOST_BITS_PER_WIDE_INT > 32
+ /* All allowed constants will fit a CONST_INT. */
+ return (GET_CODE (op) == CONST_INT
+ && (INTVAL (op) >= 0 && INTVAL (op) <= 0xffffffffL));
+#else
+ return (GET_CODE (op) == CONST_INT
+ || (GET_CODE (op) == CONST_DOUBLE && CONST_DOUBLE_HIGH (op) == 0));
+#endif
+}
+
+/* Accept integer operands in the range -0x80000000..0x7fffffff. We have
+ to check the range carefully since this predicate is used in DImode
+ contexts. */
+
+int
+const_sint32_operand (op, mode)
+ rtx op;
+ enum machine_mode mode;
+{
+ /* It doesn't make sense to ask this question with a mode that is
+ not larger than 32 bits. */
+ if (GET_MODE_BITSIZE (mode) <= 32)
+ abort ();
+
+ /* All allowed constants will fit a CONST_INT. */
+ return (GET_CODE (op) == CONST_INT
+ && (INTVAL (op) >= (-0x7fffffff - 1) && INTVAL (op) <= 0x7fffffff));
+}
+
+/* Operand predicates for implementing asymmetric pc-relative addressing
+ on m68k. The m68k supports pc-relative addressing (mode 7, register 2)
+ when used as a source operand, but not as a destination operand.
+
+ We model this by restricting the meaning of the basic predicates
+ (general_operand, memory_operand, etc) to forbid the use of this
+ addressing mode, and then define the following predicates that permit
+ this addressing mode. These predicates can then be used for the
+ source operands of the appropriate instructions.
+
+ n.b. While it is theoretically possible to change all machine patterns
+ to use this addressing more where permitted by the architecture,
+ it has only been implemented for "common" cases: SImode, HImode, and
+ QImode operands, and only for the principle operations that would
+ require this addressing mode: data movement and simple integer operations.
+
+ In parallel with these new predicates, two new constraint letters
+ were defined: 'S' and 'T'. 'S' is the -mpcrel analog of 'm'.
+ 'T' replaces 's' in the non-pcrel case. It is a no-op in the pcrel case.
+ In the pcrel case 's' is only valid in combination with 'a' registers.
+ See addsi3, subsi3, cmpsi, and movsi patterns for a better understanding
+ of how these constraints are used.
+
+ The use of these predicates is strictly optional, though patterns that
+ don't will cause an extra reload register to be allocated where one
+ was not necessary:
+
+ lea (abc:w,%pc),%a0 ; need to reload address
+ moveq &1,%d1 ; since write to pc-relative space
+ movel %d1,%a0@ ; is not allowed
+ ...
+ lea (abc:w,%pc),%a1 ; no need to reload address here
+ movel %a1@,%d0 ; since "movel (abc:w,%pc),%d0" is ok
+
+ For more info, consult tiemann@cygnus.com.
+
+
+ All of the ugliness with predicates and constraints is due to the
+ simple fact that the m68k does not allow a pc-relative addressing
+ mode as a destination. gcc does not distinguish between source and
+ destination addresses. Hence, if we claim that pc-relative address
+ modes are valid, e.g. GO_IF_LEGITIMATE_ADDRESS accepts them, then we
+ end up with invalid code. To get around this problem, we left
+ pc-relative modes as invalid addresses, and then added special
+ predicates and constraints to accept them.
+
+ A cleaner way to handle this is to modify gcc to distinguish
+ between source and destination addresses. We can then say that
+ pc-relative is a valid source address but not a valid destination
+ address, and hopefully avoid a lot of the predicate and constraint
+ hackery. Unfortunately, this would be a pretty big change. It would
+ be a useful change for a number of ports, but there aren't any current
+ plans to undertake this.
+
+ ***************************************************************************/
+
+
+/* Special case of a general operand that's used as a source operand.
+ Use this to permit reads from PC-relative memory when -mpcrel
+ is specified. */
+
+int
+general_src_operand (op, mode)
+ rtx op;
+ enum machine_mode mode;
+{
+ if (TARGET_PCREL
+ && GET_CODE (op) == MEM
+ && (GET_CODE (XEXP (op, 0)) == SYMBOL_REF
+ || GET_CODE (XEXP (op, 0)) == LABEL_REF
+ || GET_CODE (XEXP (op, 0)) == CONST))
+ return 1;
+ return general_operand (op, mode);
+}
+
+/* Special case of a nonimmediate operand that's used as a source.
+ Use this to permit reads from PC-relative memory when -mpcrel
+ is specified. */
+
+int
+nonimmediate_src_operand (op, mode)
+ rtx op;
+ enum machine_mode mode;
+{
+ if (TARGET_PCREL && GET_CODE (op) == MEM
+ && (GET_CODE (XEXP (op, 0)) == SYMBOL_REF
+ || GET_CODE (XEXP (op, 0)) == LABEL_REF
+ || GET_CODE (XEXP (op, 0)) == CONST))
+ return 1;
+ return nonimmediate_operand (op, mode);
+}
+
+/* Special case of a memory operand that's used as a source.
+ Use this to permit reads from PC-relative memory when -mpcrel
+ is specified. */
+
+int
+memory_src_operand (op, mode)
+ rtx op;
+ enum machine_mode mode;
+{
+ if (TARGET_PCREL && GET_CODE (op) == MEM
+ && (GET_CODE (XEXP (op, 0)) == SYMBOL_REF
+ || GET_CODE (XEXP (op, 0)) == LABEL_REF
+ || GET_CODE (XEXP (op, 0)) == CONST))
+ return 1;
+ return memory_operand (op, mode);
+}
+
+/* Predicate that accepts only a pc-relative address. This is needed
+ because pc-relative addresses don't satisfy the predicate
+ "general_src_operand". */
+
+int
+pcrel_address (op, mode)
+ rtx op;
+ enum machine_mode mode ATTRIBUTE_UNUSED;
+{
+ return (GET_CODE (op) == SYMBOL_REF || GET_CODE (op) == LABEL_REF
+ || GET_CODE (op) == CONST);
+}
+
+const char *
+output_andsi3 (operands)
+ rtx *operands;
+{
+ int logval;
+ if (GET_CODE (operands[2]) == CONST_INT
+ && (INTVAL (operands[2]) | 0xffff) == 0xffffffff
+ && (DATA_REG_P (operands[0])
+ || offsettable_memref_p (operands[0]))
+ && !TARGET_COLDFIRE)
+ {
+ if (GET_CODE (operands[0]) != REG)
+ operands[0] = adjust_address (operands[0], HImode, 2);
+ operands[2] = GEN_INT (INTVAL (operands[2]) & 0xffff);
+ /* Do not delete a following tstl %0 insn; that would be incorrect. */
+ CC_STATUS_INIT;
+ if (operands[2] == const0_rtx)
+ return "clr%.w %0";
+ return "and%.w %2,%0";
+ }
+ if (GET_CODE (operands[2]) == CONST_INT
+ && (logval = exact_log2 (~ INTVAL (operands[2]))) >= 0
+ && (DATA_REG_P (operands[0])
+ || offsettable_memref_p (operands[0])))
+ {
+ if (DATA_REG_P (operands[0]))
+ {
+ operands[1] = GEN_INT (logval);
+ }
+ else
+ {
+ operands[0] = adjust_address (operands[0], SImode, 3 - (logval / 8));
+ operands[1] = GEN_INT (logval % 8);
+ }
+ /* This does not set condition codes in a standard way. */
+ CC_STATUS_INIT;
+ return "bclr %1,%0";
+ }
+ return "and%.l %2,%0";
+}
+
+const char *
+output_iorsi3 (operands)
+ rtx *operands;
+{
+ register int logval;
+ if (GET_CODE (operands[2]) == CONST_INT
+ && INTVAL (operands[2]) >> 16 == 0
+ && (DATA_REG_P (operands[0])
+ || offsettable_memref_p (operands[0]))
+ && !TARGET_COLDFIRE)
+ {
+ if (GET_CODE (operands[0]) != REG)
+ operands[0] = adjust_address (operands[0], HImode, 2);
+ /* Do not delete a following tstl %0 insn; that would be incorrect. */
+ CC_STATUS_INIT;
+ if (INTVAL (operands[2]) == 0xffff)
+ return "mov%.w %2,%0";
+ return "or%.w %2,%0";
+ }
+ if (GET_CODE (operands[2]) == CONST_INT
+ && (logval = exact_log2 (INTVAL (operands[2]))) >= 0
+ && (DATA_REG_P (operands[0])
+ || offsettable_memref_p (operands[0])))
+ {
+ if (DATA_REG_P (operands[0]))
+ operands[1] = GEN_INT (logval);
+ else
+ {
+ operands[0] = adjust_address (operands[0], SImode, 3 - (logval / 8));
+ operands[1] = GEN_INT (logval % 8);
+ }
+ CC_STATUS_INIT;
+ return "bset %1,%0";
+ }
+ return "or%.l %2,%0";
+}
+
+const char *
+output_xorsi3 (operands)
+ rtx *operands;
+{
+ register int logval;
+ if (GET_CODE (operands[2]) == CONST_INT
+ && INTVAL (operands[2]) >> 16 == 0
+ && (offsettable_memref_p (operands[0]) || DATA_REG_P (operands[0]))
+ && !TARGET_COLDFIRE)
+ {
+ if (! DATA_REG_P (operands[0]))
+ operands[0] = adjust_address (operands[0], HImode, 2);
+ /* Do not delete a following tstl %0 insn; that would be incorrect. */
+ CC_STATUS_INIT;
+ if (INTVAL (operands[2]) == 0xffff)
+ return "not%.w %0";
+ return "eor%.w %2,%0";
+ }
+ if (GET_CODE (operands[2]) == CONST_INT
+ && (logval = exact_log2 (INTVAL (operands[2]))) >= 0
+ && (DATA_REG_P (operands[0])
+ || offsettable_memref_p (operands[0])))
+ {
+ if (DATA_REG_P (operands[0]))
+ operands[1] = GEN_INT (logval);
+ else
+ {
+ operands[0] = adjust_address (operands[0], SImode, 3 - (logval / 8));
+ operands[1] = GEN_INT (logval % 8);
+ }
+ CC_STATUS_INIT;
+ return "bchg %1,%0";
+ }
+ return "eor%.l %2,%0";
+}
+
+/* Output assembly to switch to section NAME with attribute FLAGS. */
+
+static void
+m68k_coff_asm_named_section (name, flags)
+ const char *name;
+ unsigned int flags;
+{
+ char flagchar;
+
+ if (flags & SECTION_WRITE)
+ flagchar = 'd';
+ else
+ flagchar = 'x';
+
+ fprintf (asm_out_file, "\t.section\t%s,\"%c\"\n", name, flagchar);
+}
+
+#ifdef CTOR_LIST_BEGIN
+static void
+m68k_svr3_asm_out_constructor (symbol, priority)
+ rtx symbol;
+ int priority ATTRIBUTE_UNUSED;
+{
+ rtx xop[2];
+
+ xop[1] = symbol;
+ xop[0] = gen_rtx_MEM (SImode, gen_rtx_PRE_DEC (SImode, stack_pointer_rtx));
+
+ init_section ();
+ output_asm_insn (output_move_simode (xop), xop);
+}
+#endif
+
+#ifdef HPUX_ASM
+static void
+m68k_hp320_internal_label (stream, prefix, labelno)
+ FILE *stream;
+ const char *prefix;
+ unsigned long labelno;
+{
+ if (prefix[0] == 'L' && prefix[1] == 'I')
+ fprintf(stream, "\tset %s%ld,.+2\n", prefix, labelno);
+ else
+ fprintf (stream, "%s%ld:\n", prefix, labelno);
+}
+
+static void
+m68k_hp320_file_start ()
+{
+ /* version 1: 68010.
+ 2: 68020 without FPU.
+ 3: 68020 with FPU. */
+ fprintf (asm_out_file, "\tversion %d\n",
+ TARGET_68020 ? (TARGET_68881 ? 3 : 2) : 1);
+}
+#endif
+
+static void
+m68k_output_mi_thunk (file, thunk, delta, vcall_offset, function)
+ FILE *file;
+ tree thunk ATTRIBUTE_UNUSED;
+ HOST_WIDE_INT delta;
+ HOST_WIDE_INT vcall_offset ATTRIBUTE_UNUSED;
+ tree function;
+{
+ rtx xops[1];
+ const char *fmt;
+
+ if (delta > 0 && delta <= 8)
+#ifdef MOTOROLA
+ asm_fprintf (file, "\taddq.l %I%d,4(%Rsp)\n", (int) delta);
+#else
+ asm_fprintf (file, "\taddql %I%d,%Rsp@(4)\n", (int) delta);
+#endif
+ else if (delta < 0 && delta >= -8)
+#ifdef MOTOROLA
+ asm_fprintf (file, "\tsubq.l %I%d,4(%Rsp)\n", (int) -delta);
+#else
+ asm_fprintf (file, "\tsubql %I%d,%Rsp@(4)\n", (int) -delta);
+#endif
+ else
+#ifdef MOTOROLA
+ asm_fprintf (file, "\tadd.l %I%wd,4(%Rsp)\n", delta);
+#else
+ asm_fprintf (file, "\taddl %I%wd,%Rsp@(4)\n", delta);
+#endif
+
+ xops[0] = DECL_RTL (function);
+
+ /* Logic taken from call patterns in m68k.md. */
+ if (flag_pic)
+ {
+ if (TARGET_PCREL)
+ fmt = "bra.l %o0";
+ else
+ {
+#ifdef MOTOROLA
+#ifdef HPUX_ASM
+ fmt = "bra.l %0";
+#else
+#ifdef USE_GAS
+ fmt = "bra.l %0@PLTPC";
+#else
+ fmt = "bra %0@PLTPC";
+#endif
+#endif
+#else
+#ifdef USE_GAS
+ fmt = "bra.l %0";
+#else
+ fmt = "jra %0,a1";
+#endif
+#endif
+ }
+ }
+ else
+ {
+#if defined (MOTOROLA) && !defined (USE_GAS)
+ fmt = "jmp %0";
+#else
+ fmt = "jra %0";
+#endif
+ }
+
+ output_asm_insn (fmt, xops);
+}