/* Subroutines for insn-output.c for Motorola 68000 family.
- Copyright (C) 1987, 1993 Free Software Foundation, Inc.
+ Copyright (C) 1987, 1993, 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 m68k.md need these. */
-#include <stdio.h>
#include "config.h"
+#include "system.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 "tm_p.h"
+#include "target.h"
+#include "target-def.h"
/* Needed for use_return_insn. */
#include "flags.h"
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 *));
\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 */
-
-finalize_pic ()
+/* 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. */
+
+struct gcc_target target = 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
+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;
+ }
+}
\f
/* This function generates the assembly code for function entry.
STREAM is a stdio stream to output the code to.
int num_saved_regs = 0;
extern char call_used_regs[];
int fsize = (size + 3) & -4;
+ int cfa_offset = INCOMING_FRAME_SP_OFFSET, 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 %0I%s+%d,%Rsp\n\ttrapcs\n",
+ XSTR (stack_limit_rtx, 0), fsize + 4);
+#else
+ asm_fprintf (stream, "\tcmpl %0I%s+%d,%Rsp\n\ttrapcs\n",
+ XSTR (stack_limit_rtx, 0), fsize + 4);
+#endif
+ }
if (frame_pointer_needed)
{
- /* Adding negative number is faster on the 68040. */
- if (fsize < 0x8000 && !TARGET_68040)
+ if (fsize == 0 && TARGET_68040)
+ {
+ /* 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]);
+#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]);
+#endif
+ }
+ else if (fsize < 0x8000)
{
#ifdef MOTOROLA
asm_fprintf (stream, "\tlink.w %s,%0I%d\n",
}
else
{
+ /* Adding negative number is faster on the 68040. */
#ifdef MOTOROLA
asm_fprintf (stream, "\tlink.w %s,%0I0\n\tadd.l %0I%d,%Rsp\n",
reg_names[FRAME_POINTER_REGNUM], -fsize);
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)
{
+#ifndef NO_ADDSUB_Q
+ if (fsize + 4 <= 8)
+ {
+ if (!TARGET_5200)
+ {
+ /* asm_fprintf() cannot handle %. */
+#ifdef MOTOROLA
+ asm_fprintf (stream, "\tsubq.w %0I%d,%Rsp\n", fsize + 4);
+#else
+ asm_fprintf (stream, "\tsubqw %0I%d,%Rsp\n", fsize + 4);
+#endif
+ }
+ else
+ {
+ /* asm_fprintf() cannot handle %. */
#ifdef MOTOROLA
- asm_fprintf (stream, "\tadd.w %0I%d,%Rsp\n", - (fsize + 4));
+ asm_fprintf (stream, "\tsubq.l %0I%d,%Rsp\n", fsize + 4);
#else
- asm_fprintf (stream, "\taddw %0I%d,%Rsp\n", - (fsize + 4));
+ asm_fprintf (stream, "\tsubql %0I%d,%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, "\tsubq.w %0I8,%Rsp\n\tsubq.w %0I%d,%Rsp\n",
+ fsize + 4 - 8);
+#else
+ asm_fprintf (stream, "\tsubqw %0I8,%Rsp\n\tsubqw %0I%d,%Rsp\n",
+ fsize + 4 - 8);
+#endif
+ }
+ else
+#endif /* not NO_ADDSUB_Q */
+ if (TARGET_68040)
+ {
+ /* Adding negative number is faster on the 68040. */
+ /* asm_fprintf() cannot handle %. */
+#ifdef MOTOROLA
+ asm_fprintf (stream, "\tadd.w %0I%d,%Rsp\n", - (fsize + 4));
+#else
+ asm_fprintf (stream, "\taddw %0I%d,%Rsp\n", - (fsize + 4));
+#endif
+ }
+ else
+ {
+#ifdef MOTOROLA
+ asm_fprintf (stream, "\tlea (%d,%Rsp),%Rsp\n", - (fsize + 4));
+#else
+ asm_fprintf (stream, "\tlea %Rsp@(%d),%Rsp\n", - (fsize + 4));
+#endif
+ }
}
else
{
+ /* asm_fprintf() cannot handle %. */
#ifdef MOTOROLA
asm_fprintf (stream, "\tadd.l %0I%d,%Rsp\n", - (fsize + 4));
#else
asm_fprintf (stream, "\taddl %0I%d,%Rsp\n", - (fsize + 4));
#endif
}
+ if (dwarf2out_do_frame ())
+ {
+ cfa_store_offset += fsize;
+ cfa_offset = cfa_store_offset;
+ dwarf2out_def_cfa ("", STACK_POINTER_REGNUM, cfa_offset);
+ }
}
#ifdef SUPPORT_SUN_FPA
for (regno = 24; regno < 56; regno++)
asm_fprintf (stream, "\tfpmoved %s,%Rsp@-\n",
reg_names[regno]);
#endif
+ if (dwarf2out_do_frame ())
+ {
+ char *l = dwarf2out_cfi_label ();
+
+ cfa_store_offset += 8;
+ if (! frame_pointer_needed)
+ {
+ cfa_offset = cfa_store_offset;
+ dwarf2out_def_cfa (l, STACK_POINTER_REGNUM, cfa_offset);
+ }
+ dwarf2out_reg_save (l, regno, -cfa_store_offset);
+ }
}
#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 (regs_ever_live[regno] && ! call_used_regs[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 %0I0x%x,-(%Rsp)\n", mask & 0xff);
#else
- asm_fprintf (stream, "\tfmovem %0I0x%x,%Rsp@-\n", mask & 0xff);
+ asm_fprintf (stream, "\tfmovem %0I0x%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])
{
mask &= ~ (1 << (15 - FRAME_POINTER_REGNUM));
num_saved_regs--;
}
+ if (flag_pic && current_function_uses_pic_offset_table)
+ {
+ mask |= 1 << (15 - PIC_OFFSET_TABLE_REGNUM);
+ num_saved_regs++;
+ }
#if NEED_PROBE
- fprintf (stream, "\ttstl sp@(%d)\n", NEED_PROBE - num_saved_regs * 4);
+#ifdef MOTOROLA
+#ifdef CRDS
+ asm_fprintf (stream, "\ttstl %d(%Rsp)\n", NEED_PROBE - num_saved_regs * 4);
+#else
+ asm_fprintf (stream, "\ttst.l %d(%Rsp)\n", NEED_PROBE - num_saved_regs * 4);
+#endif
+#else
+ asm_fprintf (stream, "\ttstl %Rsp@(%d)\n", NEED_PROBE - num_saved_regs * 4);
+#endif
#endif
+ /* 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.
/* 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_5200)
+ {
+ /* 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, "\tmovm.l %0I0x%x,-(%Rsp)\n", mask);
+ asm_fprintf (stream, "\tlea (%d,%Rsp),%Rsp\n", -num_saved_regs*4);
+ asm_fprintf (stream, "\tmovm.l %0I0x%x,(%Rsp)\n", newmask);
#else
- asm_fprintf (stream, "\tmoveml %0I0x%x,%Rsp@-\n", mask);
+ asm_fprintf (stream, "\tlea %Rsp@(%d),%Rsp\n", -num_saved_regs*4);
+ asm_fprintf (stream, "\tmoveml %0I0x%x,%Rsp@\n", newmask);
#endif
+ }
+ else
+ {
+#ifdef MOTOROLA
+ asm_fprintf (stream, "\tmovm.l %0I0x%x,-(%Rsp)\n", mask);
+#else
+ asm_fprintf (stream, "\tmoveml %0I0x%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)
{
for (regno = 0 ; regno < FIRST_PSEUDO_REGISTER ; regno++)
if (regs_ever_live[regno] && ! call_used_regs[regno])
return 0;
-
+
+ if (flag_pic && current_function_uses_pic_offset_table)
+ return 0;
+
return 1;
}
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)
return;
}
+#ifdef FUNCTION_BLOCK_PROFILER_EXIT
+ if (profile_block_flag == 2)
+ {
+ FUNCTION_BLOCK_PROFILER_EXIT (stream);
+ }
+#endif
+
#ifdef FUNCTION_EXTRA_EPILOGUE
FUNCTION_EXTRA_EPILOGUE (stream, size);
#endif
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);
- }
+ if (TARGET_68881)
+ {
+ 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; mask = 0;
if (frame_pointer_needed)
nregs++;
mask |= 1 << regno;
}
+ if (flag_pic && current_function_uses_pic_offset_table)
+ {
+ nregs++;
+ mask |= 1 << PIC_OFFSET_TABLE_REGNUM;
+ }
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
+ && ! restore_from_sp
&& (mask || fmask || fpoffset))
{
#ifdef MOTOROLA
- asm_fprintf (stream, "\t%Omove.l %0I%d,%Ra0\n", -fsize);
+ asm_fprintf (stream, "\t%Omove.l %0I%d,%Ra1\n", -fsize);
#else
- asm_fprintf (stream, "\tmovel %0I%d,%Ra0\n", -fsize);
+ asm_fprintf (stream, "\tmovel %0I%d,%Ra1\n", -fsize);
#endif
fsize = 0, big = 1;
}
- if (nregs <= 2)
+ if (TARGET_5200 || nregs <= 2)
{
/* Restore each separately in the same order moveml does.
Using two movel instructions instead of a single moveml
if (big)
{
#ifdef MOTOROLA
- asm_fprintf (stream, "\t%Omove.l -%d(%s,%Ra0.l),%s\n",
+ asm_fprintf (stream, "\t%Omove.l -%d(%s,%Ra1.l),%s\n",
offset + fsize,
reg_names[FRAME_POINTER_REGNUM],
reg_names[i]);
#else
- asm_fprintf (stream, "\tmovel %s@(-%d,%Ra0:l),%s\n",
+ asm_fprintf (stream, "\tmovel %s@(-%d,%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",
if (big)
{
#ifdef MOTOROLA
- asm_fprintf (stream, "\tmovm.l -%d(%s,%Ra0.l),%0I0x%x\n",
+ asm_fprintf (stream, "\tmovm.l -%d(%s,%Ra1.l),%0I0x%x\n",
offset + fsize,
reg_names[FRAME_POINTER_REGNUM],
mask);
#else
- asm_fprintf (stream, "\tmoveml %s@(-%d,%Ra0:l),%0I0x%x\n",
+ asm_fprintf (stream, "\tmoveml %s@(-%d,%Ra1:l),%0I0x%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);
if (big)
{
#ifdef MOTOROLA
- asm_fprintf (stream, "\tfmovm -%d(%s,%Ra0.l),%0I0x%x\n",
+ asm_fprintf (stream, "\tfmovm -%d(%s,%Ra1.l),%0I0x%x\n",
foffset + fsize,
reg_names[FRAME_POINTER_REGNUM],
fmask);
#else
- asm_fprintf (stream, "\tfmovem %s@(-%d,%Ra0:l),%0I0x%x\n",
+ asm_fprintf (stream, "\tfmovem %s@(-%d,%Ra1:l),%0I0x%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);
if (big)
{
#ifdef MOTOROLA
- asm_fprintf (stream, "\tfpmovd -%d(%s,%Ra0.l), %s\n",
+ asm_fprintf (stream, "\tfpmovd -%d(%s,%Ra1.l), %s\n",
fpoffset + fsize,
reg_names[FRAME_POINTER_REGNUM],
reg_names[regno]);
#else
- asm_fprintf (stream, "\tfpmoved %s@(-%d,%Ra0:l), %s\n",
+ asm_fprintf (stream, "\tfpmoved %s@(-%d,%Ra1:l), %s\n",
reg_names[FRAME_POINTER_REGNUM],
fpoffset + fsize, reg_names[regno]);
#endif
}
- else if (! frame_pointer_needed)
+ else if (restore_from_sp)
{
#ifdef MOTOROLA
asm_fprintf (stream, "\tfpmovd (%Rsp)+,%s\n",
reg_names[FRAME_POINTER_REGNUM]);
else if (fsize)
{
+#ifndef NO_ADDSUB_Q
+ if (fsize + 4 <= 8)
+ {
+ if (!TARGET_5200)
+ {
+#ifdef MOTOROLA
+ asm_fprintf (stream, "\taddq.w %0I%d,%Rsp\n", fsize + 4);
+#else
+ asm_fprintf (stream, "\taddqw %0I%d,%Rsp\n", fsize + 4);
+#endif
+ }
+ else
+ {
+#ifdef MOTOROLA
+ asm_fprintf (stream, "\taddq.l %0I%d,%Rsp\n", fsize + 4);
+#else
+ asm_fprintf (stream, "\taddql %0I%d,%Rsp\n", fsize + 4);
+#endif
+ }
+ }
+ 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, "\taddq.w %0I8,%Rsp\n\taddq.w %0I%d,%Rsp\n",
+ fsize + 4 - 8);
+#else
+ asm_fprintf (stream, "\taddqw %0I8,%Rsp\n\taddqw %0I%d,%Rsp\n",
+ fsize + 4 - 8);
+#endif
+ }
+ else
+#endif /* not NO_ADDSUB_Q */
if (fsize + 4 < 0x8000)
{
+ if (TARGET_68040)
+ {
+ /* asm_fprintf() cannot handle %. */
#ifdef MOTOROLA
- asm_fprintf (stream, "\tadd.w %0I%d,%Rsp\n", fsize + 4);
+ asm_fprintf (stream, "\tadd.w %0I%d,%Rsp\n", fsize + 4);
#else
- asm_fprintf (stream, "\taddw %0I%d,%Rsp\n", fsize + 4);
+ asm_fprintf (stream, "\taddw %0I%d,%Rsp\n", fsize + 4);
#endif
+ }
+ else
+ {
+#ifdef MOTOROLA
+ asm_fprintf (stream, "\tlea (%d,%Rsp),%Rsp\n", fsize + 4);
+#else
+ asm_fprintf (stream, "\tlea %Rsp@(%d),%Rsp\n", fsize + 4);
+#endif
+ }
}
else
{
+ /* asm_fprintf() cannot handle %. */
#ifdef MOTOROLA
asm_fprintf (stream, "\tadd.l %0I%d,%Rsp\n", fsize + 4);
#else
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 non-zero 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:
}
}
-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 usefull 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] = adj_offsettable_operand (operand1, 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] = adj_offsettable_operand (operand2, 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_5200 || ! 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_5200 || ! 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:
+ ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "L",
+ CODE_LABEL_NUMBER (loperands[4]));
+ output_asm_insn ("seq %5", loperands);
+ break;
+
+ case NE:
+ ASM_OUTPUT_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
+ ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "L",
+ CODE_LABEL_NUMBER (loperands[4]));
+ output_asm_insn ("sgt %5", loperands);
+ ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "L",
+ CODE_LABEL_NUMBER (loperands[6]));
+ break;
+
+ case GTU:
+ ASM_OUTPUT_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
+ ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "L",
+ CODE_LABEL_NUMBER (loperands[4]));
+ output_asm_insn ("slt %5", loperands);
+ ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "L",
+ CODE_LABEL_NUMBER (loperands[6]));
+ break;
+
+ case LTU:
+ ASM_OUTPUT_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
+ ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "L",
+ CODE_LABEL_NUMBER (loperands[4]));
+ output_asm_insn ("sge %5", loperands);
+ ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "L",
+ CODE_LABEL_NUMBER (loperands[6]));
+ break;
+
+ case GEU:
+ ASM_OUTPUT_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
+ ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "L",
+ CODE_LABEL_NUMBER (loperands[4]));
+ output_asm_insn ("sle %5", loperands);
+ ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "L",
+ CODE_LABEL_NUMBER (loperands[6]));
+ break;
+
+ case LEU:
+ ASM_OUTPUT_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
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
/* legitimize both operands of the PLUS */
if (GET_CODE (XEXP (orig, 0)) == PLUS)
{
- base = legitimize_pic_address (XEXP (XEXP (orig, 0), 0), Pmode, reg);
- orig = legitimize_pic_address (XEXP (XEXP (orig, 0), 1), Pmode,
- base == reg ? 0 : reg);
+ base = legitimize_pic_address (XEXP (XEXP (orig, 0), 0), Pmode, reg);
+ orig = legitimize_pic_address (XEXP (XEXP (orig, 0), 1), Pmode,
+ base == reg ? 0 : reg);
+ }
+ else abort ();
+
+ if (GET_CODE (orig) == CONST_INT)
+ return plus_constant_for_output (base, INTVAL (orig));
+ pic_ref = gen_rtx_PLUS (Pmode, base, orig);
+ /* Likewise, should we set special REG_NOTEs here? */
+ }
+ return pic_ref;
+}
+
+\f
+typedef enum { MOVL, SWAP, NEGW, NOTW, NOTB, MOVQ } CONST_METHOD;
+
+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_5200)
+ {
+ /* 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;
+}
+
+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 ();
+ }
+}
+
+const char *
+output_move_const_into_data_reg (operands)
+ rtx *operands;
+{
+ int i;
+
+ i = INTVAL (operands[1]);
+ switch (const_method (operands[1]))
+ {
+ case MOVQ :
+#if defined (MOTOROLA) && !defined (CRDS)
+ return "moveq%.l %1,%0";
+#else
+ return "moveq %1,%0";
+#endif
+ case NOTB :
+ operands[1] = GEN_INT (i ^ 0xff);
+#if defined (MOTOROLA) && !defined (CRDS)
+ return "moveq%.l %1,%0\n\tnot%.b %0";
+#else
+ return "moveq %1,%0\n\tnot%.b %0";
+#endif
+ case NOTW :
+ operands[1] = GEN_INT (i ^ 0xffff);
+#if defined (MOTOROLA) && !defined (CRDS)
+ return "moveq%.l %1,%0\n\tnot%.w %0";
+#else
+ return "moveq %1,%0\n\tnot%.w %0";
+#endif
+ case NEGW :
+#if defined (MOTOROLA) && !defined (CRDS)
+ return "moveq%.l %#-128,%0\n\tneg%.w %0";
+#else
+ return "moveq %#-128,%0\n\tneg%.w %0";
+#endif
+ case SWAP :
+ {
+ unsigned u = i;
+
+ operands[1] = GEN_INT ((u << 16) | (u >> 16));
+#if defined (MOTOROLA) && !defined (CRDS)
+ return "moveq%.l %1,%0\n\tswap %0";
+#else
+ return "moveq %1,%0\n\tswap %0";
+#endif
+ }
+ 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_5200)
+ || !(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_5200)
+ || !(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)
+ {
+#if defined(MOTOROLA) && !defined(CRDS)
+ return "moveq%.l %1,%0";
+#else
+ return "moveq %1,%0";
+#endif
}
- else abort ();
-
- if (GET_CODE (orig) == CONST_INT)
- return plus_constant_for_output (base, INTVAL (orig));
- pic_ref = gen_rtx (PLUS, Pmode, base, orig);
- /* Likewise, should we set special REG_NOTEs here? */
+ else if (INTVAL (operands[1]) < 0x8000
+ && INTVAL (operands[1]) >= -0x8000)
+ return "move%.w %1,%0";
}
- return pic_ref;
+ else if (CONSTANT_P (operands[1]))
+ return "move%.l %1,%0";
+#ifndef SGS_NO_LI
+ /* 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 */
+ ASM_OUTPUT_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 */
+ }
+#endif /* SGS_NO_LI */
+ return "move%.w %1,%0";
}
-\f
-/* Return the best assembler insn template
- for moving operands[1] into operands[0] as a fullword. */
-
-static char *
-singlemove_string (operands)
+const char *
+output_move_qimode (operands)
rtx *operands;
{
-#ifdef SUPPORT_SUN_FPA
- if (FPA_REG_P (operands[0]) || FPA_REG_P (operands[1]))
- return "fpmoves %1,%0";
+ 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_5200)
+ {
+ 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;
+#ifndef NO_ADDSUB_Q
+ output_asm_insn ("subq%.l %#2,%3\n\tmove%.b %1,%2", xoperands);
+#else
+ output_asm_insn ("sub%.l %#2,%3\n\tmove%.b %1,%2", xoperands);
#endif
- if (DATA_REG_P (operands[0])
- && GET_CODE (operands[1]) == CONST_INT
+ }
+ 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_5200)
+ || !(GET_CODE (operands[0]) == MEM && MEM_VOLATILE_P (operands[0]))))
+ {
+ if (operands[1] == const0_rtx)
+ return "clr%.b %0";
+ if ((!TARGET_5200 || 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)
+#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_5200)
+ || !(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_5200)
+ || !(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;
+{
+#ifdef SUPPORT_SUN_FPA
+ if (FPA_REG_P (operands[0]) || FPA_REG_P (operands[1]))
+ return "fpmoves %1,%0";
+#endif
+ 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)
{
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)
{
/* 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);
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);
else if (optype1 == CNSTOP)
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];
+ operands[1] = middlehalf[1] = latehalf[1];
/* For (set (reg:DI N) (mem:DI ... (reg:SI N) ...)),
if the upper part of reg N does not appear in the MEM, arrange to
if (optype0 == REGOP
&& (optype1 == OFFSOP || optype1 == MEMOP))
{
- if (reg_mentioned_p (operands[0], XEXP (operands[1], 0))
- && reg_mentioned_p (latehalf[0], XEXP (operands[1], 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)))
{
/* If both halves of dest are used in the src memory address,
- compute the address into latehalf of dest. */
+ compute the address into latehalf of dest.
+ Note that this can't happen if the dest is two data regs. */
compadr:
xops[0] = latehalf[0];
xops[1] = XEXP (operands[1], 0);
- output_asm_insn ("lea%L0,%a1,%0", xops);
+ output_asm_insn ("lea %a1,%0", xops);
if( GET_MODE (operands[1]) == XFmode )
{
- operands[1] = gen_rtx (MEM, XFmode, latehalf[0]);
+ 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);
}
else
{
- operands[1] = gen_rtx (MEM, DImode, latehalf[0]);
+ operands[1] = gen_rtx_MEM (DImode, latehalf[0]);
latehalf[1] = adj_offsettable_operand (operands[1], size-4);
}
}
else if (size == 12
- && reg_mentioned_p (middlehalf[0], XEXP (operands[1], 0)))
+ && reg_overlap_mentioned_p (middlehalf[0],
+ XEXP (operands[1], 0)))
{
- /* Check for two regs used by both source and dest. */
- if (reg_mentioned_p (operands[0], XEXP (operands[1], 0))
- || reg_mentioned_p (latehalf[0], XEXP (operands[1], 0)))
- goto compadr;
+ /* Check for two regs used by both source and dest.
+ Note that this can't happen if the dest is all data regs.
+ It can happen if the dest is d6, d7, a0.
+ But in that case, latehalf is an addr reg, so
+ the code at compadr does ok. */
+
+ if (reg_overlap_mentioned_p (testlow, XEXP (operands[1], 0))
+ || reg_overlap_mentioned_p (latehalf[0], XEXP (operands[1], 0)))
+ goto compadr;
/* JRV says this can't happen: */
if (addreg0 || addreg1)
- abort();
+ abort ();
/* Only the middle reg conflicts; simply put it last. */
output_asm_insn (singlemove_string (operands), operands);
output_asm_insn (singlemove_string (middlehalf), middlehalf);
return "";
}
- else if (reg_mentioned_p (operands[0], XEXP (operands[1], 0)))
+ else if (reg_overlap_mentioned_p (testlow, XEXP (operands[1], 0)))
/* If the low half of dest is mentioned in the source memory
address, the arrange to emit the move late half first. */
dest_overlapped_low = 1;
if (addreg0)
{
if (size == 12)
- output_asm_insn ("addql %#8,%0", &addreg0);
+ output_asm_insn ("addq%.l %#8,%0", &addreg0);
else
- output_asm_insn ("addql %#4,%0", &addreg0);
+ output_asm_insn ("addq%.l %#4,%0", &addreg0);
}
if (addreg1)
{
if (size == 12)
- output_asm_insn ("addql %#8,%0", &addreg1);
+ output_asm_insn ("addq%.l %#8,%0", &addreg1);
else
- output_asm_insn ("addql %#4,%0", &addreg1);
+ output_asm_insn ("addq%.l %#4,%0", &addreg1);
}
/* Do that word. */
/* Undo the adds we just did. */
if (addreg0)
- output_asm_insn ("subql %#4,%0", &addreg0);
+ output_asm_insn ("subq%.l %#4,%0", &addreg0);
if (addreg1)
- output_asm_insn ("subql %#4,%0", &addreg1);
+ output_asm_insn ("subq%.l %#4,%0", &addreg1);
if (size == 12)
{
output_asm_insn (singlemove_string (middlehalf), middlehalf);
if (addreg0)
- output_asm_insn ("subql %#4,%0", &addreg0);
+ output_asm_insn ("subq%.l %#4,%0", &addreg0);
if (addreg1)
- output_asm_insn ("subql %#4,%0", &addreg1);
+ output_asm_insn ("subq%.l %#4,%0", &addreg1);
}
/* Do low-numbered word. */
if (size == 12)
{
if (addreg0)
- output_asm_insn ("addql %#4,%0", &addreg0);
+ output_asm_insn ("addq%.l %#4,%0", &addreg0);
if (addreg1)
- output_asm_insn ("addql %#4,%0", &addreg1);
+ output_asm_insn ("addq%.l %#4,%0", &addreg1);
output_asm_insn (singlemove_string (middlehalf), middlehalf);
}
/* Make any unoffsettable addresses point at high-numbered word. */
if (addreg0)
- output_asm_insn ("addql %#4,%0", &addreg0);
+ output_asm_insn ("addq%.l %#4,%0", &addreg0);
if (addreg1)
- output_asm_insn ("addql %#4,%0", &addreg1);
+ output_asm_insn ("addq%.l %#4,%0", &addreg1);
/* Do that word. */
output_asm_insn (singlemove_string (latehalf), latehalf);
if (addreg0)
{
if (size == 12)
- output_asm_insn ("subql %#8,%0", &addreg0);
+ output_asm_insn ("subq%.l %#8,%0", &addreg0);
else
- output_asm_insn ("subql %#4,%0", &addreg0);
+ output_asm_insn ("subq%.l %#4,%0", &addreg0);
}
if (addreg1)
{
if (size == 12)
- output_asm_insn ("subql %#8,%0", &addreg1);
+ output_asm_insn ("subq%.l %#8,%0", &addreg1);
else
- output_asm_insn ("subql %#4,%0", &addreg1);
+ output_asm_insn ("subq%.l %#4,%0", &addreg1);
}
return "";
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)
+ {
+#ifndef NO_ADDSUB_Q
+ 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";
+ }
+ }
+#endif
+ 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
describe after execution of an instruction whose pattern is EXP.
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;
}
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))
{
case PLUS: case MINUS: case MULT:
case DIV: case UDIV: case MOD: case UMOD: case NEG:
- case ASHIFT: case LSHIFT: case ASHIFTRT: case LSHIFTRT:
+#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;
Thus, the cc's are set for r2.
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
cc_status.flags = CC_IN_68881;
}
\f
-char *
+const char *
output_move_const_double (operands)
rtx *operands;
{
}
}
-char *
+const char *
output_move_const_single (operands)
rtx *operands;
{
static int inited_68881_table = 0;
-char *strings_68881[7] = {
+static const char *const strings_68881[7] = {
"0.0",
"1.0",
"10.0",
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;
- /* fmovecr must be emulated on the 68040, so it shouldn't be used at all. */
- if (TARGET_68040)
+#ifdef NO_ASM_FMOVECR
+ return 0;
+#endif
+
+ /* 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;
#ifndef REAL_ARITHMETIC
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]);
}
static int inited_FPA_table = 0;
-char *strings_FPA[38] = {
+static const char *const strings_FPA[38] = {
/* small rationals */
"0.0",
"1.0",
'@' for a reference to the top word on the stack:
sp@, (sp) or (%sp) depending on the style of syntax.
'#' for an immediate operand prefix (# in MIT and Motorola syntax
- but & in SGS syntax).
+ but & in SGS syntax, $ in CRDS/UNOS syntax).
'!' for the cc register (used in an `and to cc' insn).
'$' for the letter `s' in an op code, but only on the 68040.
'&' for the letter `d' in an op code, but only on the 68040.
'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)
+ 'o' for operands to go directly to output_operand_address (bypassing
+ print_operand_address--used only for SYMBOL_REFs under TARGET_PCREL)
'w' for FPA insn (print a CONST_DOUBLE as a SunFPA constant rather
than directly). Second part of 'y' below.
'x' for float insn (print a CONST_DOUBLE as a float rather than in hex),
rtx op; /* operand to print */
int letter; /* %<letter> or 0 */
{
+#ifdef SUPPORT_SUN_FPA
int i;
+#endif
if (letter == '.')
{
-#ifdef MOTOROLA
+#if defined (MOTOROLA) && !defined (CRDS)
asm_fprintf (file, ".");
#endif
}
{
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)
reg_names[REGNO (op)+1]);
}
else
+#endif
{
- fprintf (file, "%s", reg_names[REGNO (op)]);
+ 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
+ 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
{
- 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, "%0I");
+ if (TARGET_PCREL
+ && (GET_CODE (op) == SYMBOL_REF || GET_CODE (op) == CONST))
+ print_operand_address (file, op);
+ else
+ output_addr_const (file, op);
}
}
define such "LDnnn" to be either "Lnnn-LInnn-2.b", "Lnnn", or any other
string, as necessary. This is accomplished via the ASM_OUTPUT_CASE_END
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.
They also do not like things like "pea 1.w", so we simple leave off
the .w on small constants.
offset is output in word mode (eg movel a5@(_foo:w), a0). When generating
-fPIC code the offset is output in long mode (eg movel a5@(_foo:l), a0) */
+#ifndef ASM_OUTPUT_CASE_FETCH
+#ifdef MOTOROLA
+#ifdef SGS
+#define ASM_OUTPUT_CASE_FETCH(file, labelno, regname)\
+ asm_fprintf (file, "%LLD%d(%Rpc,%s.", labelno, regname)
+#else
+#define ASM_OUTPUT_CASE_FETCH(file, labelno, regname)\
+ asm_fprintf (file, "%LL%d-%LLI%d.b(%Rpc,%s.", labelno, labelno, regname)
+#endif
+#else
+#define ASM_OUTPUT_CASE_FETCH(file, labelno, regname)\
+ asm_fprintf (file, "%Rpc@(%LL%d-%LLI%d-2:b,%s:", labelno, labelno, regname)
+#endif
+#endif /* ASM_OUTPUT_CASE_FETCH */
+
void
print_operand_address (file, addr)
FILE *file;
}
if (GET_CODE (ireg) == SIGN_EXTEND)
{
-#ifdef MOTOROLA
-#ifdef SGS
- asm_fprintf (file, "%LLD%d(%Rpc,%s.w",
- CODE_LABEL_NUMBER (XEXP (addr, 0)),
- reg_names[REGNO (XEXP (ireg, 0))]);
-#else
- asm_fprintf (file, "%LL%d-%LLI%d.b(%Rpc,%s.w",
- CODE_LABEL_NUMBER (XEXP (addr, 0)),
- CODE_LABEL_NUMBER (XEXP (addr, 0)),
- reg_names[REGNO (XEXP (ireg, 0))]);
-#endif
-#else
- asm_fprintf (file, "%Rpc@(%LL%d-%LLI%d-2:b,%s:w",
- CODE_LABEL_NUMBER (XEXP (addr, 0)),
+ ASM_OUTPUT_CASE_FETCH (file,
CODE_LABEL_NUMBER (XEXP (addr, 0)),
reg_names[REGNO (XEXP (ireg, 0))]);
-#endif
+ fprintf (file, "w");
}
else
{
-#ifdef MOTOROLA
-#ifdef SGS
- asm_fprintf (file, "%LLD%d(%Rpc,%s.l",
- CODE_LABEL_NUMBER (XEXP (addr, 0)),
- reg_names[REGNO (ireg)]);
-#else
- asm_fprintf (file, "%LL%d-%LLI%d.b(%Rpc,%s.l",
- CODE_LABEL_NUMBER (XEXP (addr, 0)),
- CODE_LABEL_NUMBER (XEXP (addr, 0)),
- reg_names[REGNO (ireg)]);
-#endif
-#else
- asm_fprintf (file, "%Rpc@(%LL%d-%LLI%d-2:b,%s:l",
- CODE_LABEL_NUMBER (XEXP (addr, 0)),
+ ASM_OUTPUT_CASE_FETCH (file,
CODE_LABEL_NUMBER (XEXP (addr, 0)),
reg_names[REGNO (ireg)]);
-#endif
+ fprintf (file, "l");
}
if (scale != 1)
{
if (breg != 0 && ireg == 0 && GET_CODE (addr) == LABEL_REF
&& ! (flag_pic && breg == pic_offset_table_rtx))
{
-#ifdef MOTOROLA
-#ifdef SGS
- asm_fprintf (file, "%LLD%d(%Rpc,%s.l",
- CODE_LABEL_NUMBER (XEXP (addr, 0)),
- reg_names[REGNO (breg)]);
-#else
- asm_fprintf (file, "%LL%d-%LLI%d.b(%Rpc,%s.l",
- CODE_LABEL_NUMBER (XEXP (addr, 0)),
- CODE_LABEL_NUMBER (XEXP (addr, 0)),
- reg_names[REGNO (breg)]);
-#endif
-#else
- asm_fprintf (file, "%Rpc@(%LL%d-%LLI%d-2:b,%s:l",
- CODE_LABEL_NUMBER (XEXP (addr, 0)),
+ ASM_OUTPUT_CASE_FETCH (file,
CODE_LABEL_NUMBER (XEXP (addr, 0)),
reg_names[REGNO (breg)]);
-#endif
- putc (')', file);
+ fprintf (file, "l)");
break;
}
if (ireg != 0 || breg != 0)
{
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)
else if (reg1 != 0 && GET_CODE (addr) == LABEL_REF
&& ! (flag_pic && reg1 == pic_offset_table_rtx))
{
-#ifdef MOTOROLA
-#ifdef SGS
- asm_fprintf (file, "%LLD%d(%Rpc,%s.l)",
- CODE_LABEL_NUMBER (XEXP (addr, 0)),
- reg_names[REGNO (reg1)]);
-#else
- asm_fprintf (file, "%LL%d-%LLI%d.b(%Rpc,%s.l)",
- CODE_LABEL_NUMBER (XEXP (addr, 0)),
- CODE_LABEL_NUMBER (XEXP (addr, 0)),
- reg_names[REGNO (reg1)]);
-#endif
-#else
- asm_fprintf (file, "%Rpc@(%LL%d-%LLI%d-2:b,%s:l)",
- CODE_LABEL_NUMBER (XEXP (addr, 0)),
+ ASM_OUTPUT_CASE_FETCH (file,
CODE_LABEL_NUMBER (XEXP (addr, 0)),
reg_names[REGNO (reg1)]);
-#endif
+ 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
fprintf (file, "%d:w", INTVAL (addr));
#endif
}
- else
+ else if (GET_CODE (addr) == CONST_INT)
+ {
+ fprintf (file,
+#if HOST_BITS_PER_WIDE_INT == HOST_BITS_PER_INT
+ "%d",
+#else
+ "%ld",
+#endif
+ 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 ATTRIBUTE_UNUSED;
+{
+#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 ATTRIBUTE_UNUSED;
+{
+ /* 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 destintation 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_5200)
+ {
+ if (GET_CODE (operands[0]) != REG)
+ operands[0] = adj_offsettable_operand (operands[0], 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] = adj_offsettable_operand (operands[0], 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_5200)
+ {
+ if (GET_CODE (operands[0]) != REG)
+ operands[0] = adj_offsettable_operand (operands[0], 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] = adj_offsettable_operand (operands[0], 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_5200)
+ {
+ if (! DATA_REG_P (operands[0]))
+ operands[0] = adj_offsettable_operand (operands[0], 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] = adj_offsettable_operand (operands[0], 3 - (logval / 8));
+ operands[1] = GEN_INT (logval % 8);
+ }
+ CC_STATUS_INIT;
+ return "bchg %1,%0";
+ }
+ return "eor%.l %2,%0";
+}