summaryrefslogtreecommitdiff
path: root/gcc/config/m68k/m68k.c
diff options
context:
space:
mode:
authorYamaArashi <shadow962@live.com>2016-01-06 01:47:28 -0800
committerYamaArashi <shadow962@live.com>2016-01-06 01:47:28 -0800
commitbe8b04496302184c6e8f04d6179f9c3afc50aeb6 (patch)
tree726e2468c0c07add773c0dbd86ab6386844259ae /gcc/config/m68k/m68k.c
initial commit
Diffstat (limited to 'gcc/config/m68k/m68k.c')
-rwxr-xr-xgcc/config/m68k/m68k.c3393
1 files changed, 3393 insertions, 0 deletions
diff --git a/gcc/config/m68k/m68k.c b/gcc/config/m68k/m68k.c
new file mode 100755
index 0000000..244fa7f
--- /dev/null
+++ b/gcc/config/m68k/m68k.c
@@ -0,0 +1,3393 @@
+/* Subroutines for insn-output.c for Motorola 68000 family.
+ Copyright (C) 1987, 93-98, 1999 Free Software Foundation, Inc.
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA. */
+
+
+/* Some output-actions in m68k.md need these. */
+#include "config.h"
+#include "system.h"
+#include "tree.h"
+#include "rtl.h"
+#include "regs.h"
+#include "hard-reg-set.h"
+#include "real.h"
+#include "insn-config.h"
+#include "conditions.h"
+#include "insn-flags.h"
+#include "output.h"
+#include "insn-attr.h"
+#include "recog.h"
+#include "toplev.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 ();
+
+
+/* Alignment to use for loops and jumps */
+/* Specify power of two alignment used for loops. */
+char *m68k_align_loops_string;
+/* Specify power of two alignment used for non-loop jumps. */
+char *m68k_align_jumps_string;
+/* Specify power of two alignment used for functions. */
+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;
+
+/* 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 ()
+{
+ int def_align;
+
+ def_align = 1;
+
+ /* Validate -malign-loops= value, or provide default */
+ if (m68k_align_loops_string)
+ {
+ m68k_align_loops = atoi (m68k_align_loops_string);
+ if (m68k_align_loops < 1 || m68k_align_loops > MAX_CODE_ALIGN)
+ fatal ("-malign-loops=%d is not between 1 and %d",
+ m68k_align_loops, MAX_CODE_ALIGN);
+ }
+ else
+ m68k_align_loops = def_align;
+
+ /* Validate -malign-jumps= value, or provide default */
+ if (m68k_align_jumps_string)
+ {
+ m68k_align_jumps = atoi (m68k_align_jumps_string);
+ if (m68k_align_jumps < 1 || m68k_align_jumps > MAX_CODE_ALIGN)
+ fatal ("-malign-jumps=%d is not between 1 and %d",
+ m68k_align_jumps, MAX_CODE_ALIGN);
+ }
+ else
+ m68k_align_jumps = def_align;
+
+ /* Validate -malign-functions= value, or provide default */
+ if (m68k_align_funcs_string)
+ {
+ m68k_align_funcs = atoi (m68k_align_funcs_string);
+ if (m68k_align_funcs < 1 || m68k_align_funcs > MAX_CODE_ALIGN)
+ fatal ("-malign-functions=%d is not between 1 and %d",
+ m68k_align_funcs, MAX_CODE_ALIGN);
+ }
+ else
+ m68k_align_funcs = def_align;
+}
+
+/* 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 */
+
+void
+finalize_pic ()
+{
+ if (flag_pic && current_function_uses_pic_offset_table)
+ {
+ rtx insn = gen_rtx_USE (VOIDmode, pic_offset_table_rtx);
+ emit_insn_after (insn, get_insns ());
+ emit_insn (insn);
+ }
+}
+
+
+/* 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.
+ Refer to the array `regs_ever_live' to determine which registers
+ to save; `regs_ever_live[I]' is nonzero if register number I
+ is ever used in the function. This function is responsible for
+ knowing which registers should not be saved even if used. */
+
+
+/* Note that the order of the bit mask for fmovem is the opposite
+ of the order for movem! */
+
+
+void
+output_function_prologue (stream, size)
+ FILE *stream;
+ int size;
+{
+ register int regno;
+ register int mask = 0;
+ 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 (frame_pointer_needed)
+ {
+ 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",
+ reg_names[FRAME_POINTER_REGNUM], -fsize);
+#else
+ asm_fprintf (stream, "\tlink %s,%0I%d\n",
+ reg_names[FRAME_POINTER_REGNUM], -fsize);
+#endif
+ }
+ else if (TARGET_68020)
+ {
+#ifdef MOTOROLA
+ asm_fprintf (stream, "\tlink.l %s,%0I%d\n",
+ reg_names[FRAME_POINTER_REGNUM], -fsize);
+#else
+ asm_fprintf (stream, "\tlink %s,%0I%d\n",
+ reg_names[FRAME_POINTER_REGNUM], -fsize);
+#endif
+ }
+ 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);
+#else
+ asm_fprintf (stream, "\tlink %s,%0I0\n\taddl %0I%d,%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_def_cfa (l, FRAME_POINTER_REGNUM, cfa_offset);
+ dwarf2out_reg_save (l, FRAME_POINTER_REGNUM, -cfa_store_offset);
+ cfa_store_offset += fsize;
+ }
+ }
+ else if (fsize)
+ {
+ 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, "\tsubq.l %0I%d,%Rsp\n", fsize + 4);
+#else
+ 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);
+#else
+ asm_fprintf (stream, "\tsubqw %0I8,%Rsp\n\tsubqw %0I%d,%Rsp\n",
+ fsize + 4);
+#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++)
+ 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
+ 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
+ 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);
+#else
+ 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;
+ }
+ for (regno = 0; regno < 16; regno++)
+ if (regs_ever_live[regno] && ! call_used_regs[regno])
+ {
+ mask |= 1 << (15 - regno);
+ num_saved_regs++;
+ }
+ if (frame_pointer_needed)
+ {
+ mask &= ~ (1 << (15 - FRAME_POINTER_REGNUM));
+ num_saved_regs--;
+ }
+
+#if NEED_PROBE
+#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 (num_saved_regs <= 2)
+ {
+ /* Store each separately in the same order moveml uses.
+ Using two movel instructions instead of a single moveml
+ is about 15% faster for the 68020 and 68030 at no expense
+ in code size */
+
+ int i;
+
+ /* Undo the work from above. */
+ for (i = 0; i< 16; i++)
+ if (mask & (1 << i))
+ {
+ asm_fprintf (stream,
+#ifdef MOTOROLA
+ "\t%Omove.l %s,-(%Rsp)\n",
+#else
+ "\tmovel %s,%Rsp@-\n",
+#endif
+ 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, "\tlea (%d,%Rsp),%Rsp\n", -num_saved_regs*4);
+ asm_fprintf (stream, "\tmovm.l %0I0x%x,(%Rsp)\n", newmask);
+#else
+ 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)
+ {
+#ifdef MOTOROLA
+ 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",
+ reg_names[PIC_OFFSET_TABLE_REGNUM]);
+ asm_fprintf (stream, "\tlea %Rpc@(0,%s:l),%s\n",
+ reg_names[PIC_OFFSET_TABLE_REGNUM],
+ reg_names[PIC_OFFSET_TABLE_REGNUM]);
+#endif
+ }
+}
+
+/* Return true if this function's epilogue can be output as RTL. */
+
+int
+use_return_insn ()
+{
+ int regno;
+
+ 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])
+ 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.
+
+ 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)
+ FILE *stream;
+ 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;
+ 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)
+ insn = prev_nonnote_insn (insn);
+ if (insn && GET_CODE (insn) == BARRIER)
+ {
+ /* 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");
+ 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
+ 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;
+ 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)
+ regs_ever_live[FRAME_POINTER_REGNUM] = 0;
+ for (regno = 0; regno < 16; regno++)
+ if (regs_ever_live[regno] && ! call_used_regs[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
+ && ! restore_from_sp
+ && (mask || fmask || fpoffset))
+ {
+#ifdef MOTOROLA
+ asm_fprintf (stream, "\t%Omove.l %0I%d,%Ra1\n", -fsize);
+#else
+ asm_fprintf (stream, "\tmovel %0I%d,%Ra1\n", -fsize);
+#endif
+ fsize = 0, big = 1;
+ }
+ if (TARGET_5200 || 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. */
+
+ int i;
+
+ /* 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",
+ offset + fsize,
+ reg_names[FRAME_POINTER_REGNUM],
+ reg_names[i]);
+#else
+ asm_fprintf (stream, "\tmovel %s@(-%d,%Ra1:l),%s\n",
+ reg_names[FRAME_POINTER_REGNUM],
+ offset + fsize, reg_names[i]);
+#endif
+ }
+ else if (restore_from_sp)
+ {
+#ifdef MOTOROLA
+ asm_fprintf (stream, "\t%Omove.l (%Rsp)+,%s\n",
+ reg_names[i]);
+#else
+ asm_fprintf (stream, "\tmovel %Rsp@+,%s\n",
+ reg_names[i]);
+#endif
+ }
+ else
+ {
+#ifdef MOTOROLA
+ asm_fprintf (stream, "\t%Omove.l -%d(%s),%s\n",
+ offset + fsize,
+ reg_names[FRAME_POINTER_REGNUM],
+ reg_names[i]);
+#else
+ asm_fprintf (stream, "\tmovel %s@(-%d),%s\n",
+ reg_names[FRAME_POINTER_REGNUM],
+ offset + fsize, reg_names[i]);
+#endif
+ }
+ offset = offset - 4;
+ }
+ }
+ else if (mask)
+ {
+ if (big)
+ {
+#ifdef MOTOROLA
+ 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,%Ra1:l),%0I0x%x\n",
+ reg_names[FRAME_POINTER_REGNUM],
+ offset + fsize, mask);
+#endif
+ }
+ else if (restore_from_sp)
+ {
+#ifdef MOTOROLA
+ asm_fprintf (stream, "\tmovm.l (%Rsp)+,%0I0x%x\n", mask);
+#else
+ asm_fprintf (stream, "\tmoveml %Rsp@+,%0I0x%x\n", mask);
+#endif
+ }
+ else
+ {
+#ifdef MOTOROLA
+ asm_fprintf (stream, "\tmovm.l -%d(%s),%0I0x%x\n",
+ offset + fsize,
+ reg_names[FRAME_POINTER_REGNUM],
+ mask);
+#else
+ asm_fprintf (stream, "\tmoveml %s@(-%d),%0I0x%x\n",
+ reg_names[FRAME_POINTER_REGNUM],
+ offset + fsize, mask);
+#endif
+ }
+ }
+ if (fmask)
+ {
+ if (big)
+ {
+#ifdef MOTOROLA
+ 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,%Ra1:l),%0I0x%x\n",
+ reg_names[FRAME_POINTER_REGNUM],
+ foffset + fsize, fmask);
+#endif
+ }
+ else if (restore_from_sp)
+ {
+#ifdef MOTOROLA
+ asm_fprintf (stream, "\tfmovm (%Rsp)+,%0I0x%x\n", fmask);
+#else
+ asm_fprintf (stream, "\tfmovem %Rsp@+,%0I0x%x\n", fmask);
+#endif
+ }
+ else
+ {
+#ifdef MOTOROLA
+ asm_fprintf (stream, "\tfmovm -%d(%s),%0I0x%x\n",
+ foffset + fsize,
+ reg_names[FRAME_POINTER_REGNUM],
+ fmask);
+#else
+ asm_fprintf (stream, "\tfmovem %s@(-%d),%0I0x%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)
+ {
+#ifdef MOTOROLA
+ 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,%Ra1:l), %s\n",
+ reg_names[FRAME_POINTER_REGNUM],
+ fpoffset + fsize, reg_names[regno]);
+#endif
+ }
+ else if (restore_from_sp)
+ {
+#ifdef MOTOROLA
+ asm_fprintf (stream, "\tfpmovd (%Rsp)+,%s\n",
+ reg_names[regno]);
+#else
+ asm_fprintf (stream, "\tfpmoved %Rsp@+, %s\n",
+ reg_names[regno]);
+#endif
+ }
+ else
+ {
+#ifdef MOTOROLA
+ asm_fprintf (stream, "\tfpmovd -%d(%s), %s\n",
+ fpoffset + fsize,
+ reg_names[FRAME_POINTER_REGNUM],
+ reg_names[regno]);
+#else
+ asm_fprintf (stream, "\tfpmoved %s@(-%d), %s\n",
+ reg_names[FRAME_POINTER_REGNUM],
+ fpoffset + fsize, reg_names[regno]);
+#endif
+ }
+ fpoffset -= 8;
+ }
+ if (frame_pointer_needed)
+ fprintf (stream, "\tunlk %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);
+#else
+ asm_fprintf (stream, "\taddqw %0I8,%Rsp\n\taddqw %0I%d,%Rsp\n",
+ fsize + 4);
+#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);
+#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 (current_function_pops_args)
+ asm_fprintf (stream, "\trtd %0I%d\n", current_function_pops_args);
+ else
+ fprintf (stream, "\trts\n");
+}
+
+/* Similar to general_operand, but exclude stack_pointer_rtx. */
+
+int
+not_sp_operand (op, mode)
+ register rtx op;
+ enum machine_mode mode;
+{
+ return op != stack_pointer_rtx && general_operand (op, mode);
+}
+
+/* Return TRUE if X is a valid comparison operator for the dbcc
+ instruction.
+
+ Note it rejects floating point comparison operators.
+ (In the future we could use Fdbcc).
+
+ It also rejects some comparisons when CC_NO_OVERFLOW is set. */
+
+int
+valid_dbcc_comparison_p (x, mode)
+ rtx x;
+ enum machine_mode mode ATTRIBUTE_UNUSED;
+{
+ switch (GET_CODE (x))
+ {
+ case EQ: case NE: case GTU: case LTU:
+ case GEU: case LEU:
+ return 1;
+
+ /* Reject some when CC_NO_OVERFLOW is set. This may be over
+ conservative */
+ case GT: case LT: case GE: case LE:
+ return ! (cc_prev_status.flags & CC_NO_OVERFLOW);
+ default:
+ return 0;
+ }
+}
+
+/* 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 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:
+#ifdef MOTOROLA
+ output_asm_insn ("dbeq %0,%l1\n\tjbeq %l2", operands);
+#else
+ output_asm_insn ("dbeq %0,%l1\n\tjeq %l2", operands);
+#endif
+ break;
+
+ case NE:
+#ifdef MOTOROLA
+ output_asm_insn ("dbne %0,%l1\n\tjbne %l2", operands);
+#else
+ output_asm_insn ("dbne %0,%l1\n\tjne %l2", operands);
+#endif
+ break;
+
+ case GT:
+#ifdef MOTOROLA
+ output_asm_insn ("dbgt %0,%l1\n\tjbgt %l2", operands);
+#else
+ output_asm_insn ("dbgt %0,%l1\n\tjgt %l2", operands);
+#endif
+ break;
+
+ case GTU:
+#ifdef MOTOROLA
+ output_asm_insn ("dbhi %0,%l1\n\tjbhi %l2", operands);
+#else
+ output_asm_insn ("dbhi %0,%l1\n\tjhi %l2", operands);
+#endif
+ break;
+
+ case LT:
+#ifdef MOTOROLA
+ output_asm_insn ("dblt %0,%l1\n\tjblt %l2", operands);
+#else
+ output_asm_insn ("dblt %0,%l1\n\tjlt %l2", operands);
+#endif
+ break;
+
+ case LTU:
+#ifdef MOTOROLA
+ output_asm_insn ("dbcs %0,%l1\n\tjbcs %l2", operands);
+#else
+ output_asm_insn ("dbcs %0,%l1\n\tjcs %l2", operands);
+#endif
+ break;
+
+ case GE:
+#ifdef MOTOROLA
+ output_asm_insn ("dbge %0,%l1\n\tjbge %l2", operands);
+#else
+ output_asm_insn ("dbge %0,%l1\n\tjge %l2", operands);
+#endif
+ break;
+
+ case GEU:
+#ifdef MOTOROLA
+ output_asm_insn ("dbcc %0,%l1\n\tjbcc %l2", operands);
+#else
+ output_asm_insn ("dbcc %0,%l1\n\tjcc %l2", operands);
+#endif
+ break;
+
+ case LE:
+#ifdef MOTOROLA
+ output_asm_insn ("dble %0,%l1\n\tjble %l2", operands);
+#else
+ output_asm_insn ("dble %0,%l1\n\tjle %l2", operands);
+#endif
+ break;
+
+ case LEU:
+#ifdef MOTOROLA
+ output_asm_insn ("dbls %0,%l1\n\tjbls %l2", operands);
+#else
+ output_asm_insn ("dbls %0,%l1\n\tjls %l2", operands);
+#endif
+ break;
+
+ default:
+ abort ();
+ }
+
+ /* If the decrement is to be done in SImode, then we have
+ to compensate for the fact that dbcc decrements in HImode. */
+ switch (GET_MODE (operands[0]))
+ {
+ case SImode:
+#ifdef MOTOROLA
+ output_asm_insn ("clr%.w %0\n\tsubq%.l %#1,%0\n\tjbpl %l1", operands);
+#else
+ output_asm_insn ("clr%.w %0\n\tsubq%.l %#1,%0\n\tjpl %l1", operands);
+#endif
+ break;
+
+ case HImode:
+ break;
+
+ default:
+ abort ();
+ }
+}
+
+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 "";
+}
+
+char *
+output_btst (operands, countop, dataop, insn, signpos)
+ rtx *operands;
+ rtx countop, dataop;
+ rtx insn;
+ int signpos;
+{
+ operands[0] = countop;
+ operands[1] = dataop;
+
+ if (GET_CODE (countop) == CONST_INT)
+ {
+ register int count = INTVAL (countop);
+ /* If COUNT is bigger than size of storage unit in use,
+ advance to the containing unit of same size. */
+ if (count > signpos)
+ {
+ int offset = (count & ~signpos) / 8;
+ count = count & signpos;
+ operands[1] = dataop = adj_offsettable_operand (dataop, offset);
+ }
+ if (count == signpos)
+ cc_status.flags = CC_NOT_POSITIVE | CC_Z_IN_NOT_N;
+ else
+ cc_status.flags = CC_NOT_NEGATIVE | CC_Z_IN_NOT_N;
+
+ /* These three statements used to use next_insns_test_no...
+ but it appears that this should do the same job. */
+ if (count == 31
+ && next_insn_tests_no_inequality (insn))
+ return "tst%.l %1";
+ if (count == 15
+ && next_insn_tests_no_inequality (insn))
+ return "tst%.w %1";
+ if (count == 7
+ && next_insn_tests_no_inequality (insn))
+ return "tst%.b %1";
+
+ cc_status.flags = CC_NOT_NEGATIVE;
+ }
+ return "btst %0,%1";
+}
+
+/* Returns 1 if OP is either a symbol reference or a sum of a symbol
+ reference and a constant. */
+
+int
+symbolic_operand (op, mode)
+ register rtx op;
+ enum machine_mode mode ATTRIBUTE_UNUSED;
+{
+ switch (GET_CODE (op))
+ {
+ case SYMBOL_REF:
+ case LABEL_REF:
+ return 1;
+
+ case CONST:
+ op = XEXP (op, 0);
+ return ((GET_CODE (XEXP (op, 0)) == SYMBOL_REF
+ || GET_CODE (XEXP (op, 0)) == LABEL_REF)
+ && GET_CODE (XEXP (op, 1)) == CONST_INT);
+
+#if 0 /* Deleted, with corresponding change in m68k.h,
+ so as to fit the specs. No CONST_DOUBLE is ever symbolic. */
+ case CONST_DOUBLE:
+ return GET_MODE (op) == mode;
+#endif
+
+ default:
+ return 0;
+ }
+}
+
+/* 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;
+ }
+}
+
+
+/* Legitimize PIC addresses. If the address is already
+ position-independent, we return ORIG. Newly generated
+ position-independent addresses go to REG. If we need more
+ than one register, we lose.
+
+ An address is legitimized by making an indirect reference
+ through the Global Offset Table with the name of the symbol
+ used as an offset.
+
+ The assembler and linker are responsible for placing the
+ address of the symbol in the GOT. The function prologue
+ is responsible for initializing a5 to the starting address
+ of the GOT.
+
+ The assembler is also responsible for translating a symbol name
+ into a constant displacement from the start of the GOT.
+
+ A quick example may make things a little clearer:
+
+ When not generating PIC code to store the value 12345 into _foo
+ we would generate the following code:
+
+ movel #12345, _foo
+
+ When generating PIC two transformations are made. First, the compiler
+ loads the address of foo into a register. So the first transformation makes:
+
+ lea _foo, a0
+ movel #12345, a0@
+
+ The code in movsi will intercept the lea instruction and call this
+ routine which will transform the instructions into:
+
+ movel a5@(_foo:w), a0
+ movel #12345, a0@
+
+
+ That (in a nutshell) is how *all* symbol and label references are
+ handled. */
+
+rtx
+legitimize_pic_address (orig, mode, reg)
+ rtx orig, reg;
+ enum machine_mode mode ATTRIBUTE_UNUSED;
+{
+ rtx pic_ref = orig;
+
+ /* First handle a simple SYMBOL_REF or LABEL_REF */
+ if (GET_CODE (orig) == SYMBOL_REF || GET_CODE (orig) == LABEL_REF)
+ {
+ if (reg == 0)
+ abort ();
+
+ pic_ref = gen_rtx_MEM (Pmode,
+ gen_rtx_PLUS (Pmode,
+ pic_offset_table_rtx, orig));
+ current_function_uses_pic_offset_table = 1;
+ if (reload_in_progress)
+ regs_ever_live[PIC_OFFSET_TABLE_REGNUM] = 1;
+ RTX_UNCHANGING_P (pic_ref) = 1;
+ emit_move_insn (reg, pic_ref);
+ return reg;
+ }
+ else if (GET_CODE (orig) == CONST)
+ {
+ rtx base;
+
+ /* Make sure this is CONST has not already been legitimized */
+ if (GET_CODE (XEXP (orig, 0)) == PLUS
+ && XEXP (XEXP (orig, 0), 0) == pic_offset_table_rtx)
+ return orig;
+
+ if (reg == 0)
+ abort ();
+
+ /* 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);
+ }
+ 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;
+}
+
+
+typedef enum { MOVL, SWAP, NEGW, NOTW, NOTB, MOVQ } CONST_METHOD;
+
+#define USE_MOVQ(i) ((unsigned)((i) + 128) <= 255)
+
+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 ();
+ }
+}
+
+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 ();
+ }
+}
+
+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";
+}
+
+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";
+}
+
+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 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";
+#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";
+}
+
+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_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
+ }
+ 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)
+ return "moveq%.l %1,%0";
+#else
+ return "moveq %1,%0";
+#endif
+ }
+ 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";
+ /* 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";
+}
+
+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";
+}
+
+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 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 *
+output_move_double (operands)
+ rtx *operands;
+{
+ enum
+ {
+ REGOP, OFFSOP, MEMOP, PUSHOP, POPOP, CNSTOP, RNDOP
+ } optype0, optype1;
+ rtx latehalf[2];
+ rtx middlehalf[2];
+ rtx xops[2];
+ rtx addreg0 = 0, addreg1 = 0;
+ int dest_overlapped_low = 0;
+ int size = GET_MODE_SIZE (GET_MODE (operands[0]));
+
+ middlehalf[0] = 0;
+ middlehalf[1] = 0;
+
+ /* First classify both operands. */
+
+ if (REG_P (operands[0]))
+ optype0 = REGOP;
+ else if (offsettable_memref_p (operands[0]))
+ optype0 = OFFSOP;
+ else if (GET_CODE (XEXP (operands[0], 0)) == POST_INC)
+ optype0 = POPOP;
+ else if (GET_CODE (XEXP (operands[0], 0)) == PRE_DEC)
+ optype0 = PUSHOP;
+ else if (GET_CODE (operands[0]) == MEM)
+ optype0 = MEMOP;
+ else
+ optype0 = RNDOP;
+
+ if (REG_P (operands[1]))
+ optype1 = REGOP;
+ else if (CONSTANT_P (operands[1]))
+ optype1 = CNSTOP;
+ else if (offsettable_memref_p (operands[1]))
+ optype1 = OFFSOP;
+ else if (GET_CODE (XEXP (operands[1], 0)) == POST_INC)
+ optype1 = POPOP;
+ else if (GET_CODE (XEXP (operands[1], 0)) == PRE_DEC)
+ optype1 = PUSHOP;
+ else if (GET_CODE (operands[1]) == MEM)
+ optype1 = MEMOP;
+ else
+ optype1 = RNDOP;
+
+ /* Check for the cases that the operand constraints are not
+ supposed to allow to happen. Abort if we get one,
+ because generating code for these cases is painful. */
+
+ if (optype0 == RNDOP || optype1 == RNDOP)
+ abort ();
+
+ /* If one operand is decrementing and one is incrementing
+ decrement the former register explicitly
+ and change that operand into ordinary indexing. */
+
+ if (optype0 == PUSHOP && optype1 == POPOP)
+ {
+ operands[0] = XEXP (XEXP (operands[0], 0), 0);
+ if (size == 12)
+ output_asm_insn ("sub%.l %#12,%0", operands);
+ else
+ output_asm_insn ("subq%.l %#8,%0", operands);
+ if (GET_MODE (operands[1]) == XFmode)
+ operands[0] = gen_rtx_MEM (XFmode, operands[0]);
+ else if (GET_MODE (operands[0]) == DFmode)
+ operands[0] = gen_rtx_MEM (DFmode, operands[0]);
+ else
+ operands[0] = gen_rtx_MEM (DImode, operands[0]);
+ optype0 = OFFSOP;
+ }
+ if (optype0 == POPOP && optype1 == PUSHOP)
+ {
+ operands[1] = XEXP (XEXP (operands[1], 0), 0);
+ if (size == 12)
+ output_asm_insn ("sub%.l %#12,%1", operands);
+ else
+ output_asm_insn ("subq%.l %#8,%1", operands);
+ if (GET_MODE (operands[1]) == XFmode)
+ operands[1] = gen_rtx_MEM (XFmode, operands[1]);
+ else if (GET_MODE (operands[1]) == DFmode)
+ operands[1] = gen_rtx_MEM (DFmode, operands[1]);
+ else
+ operands[1] = gen_rtx_MEM (DImode, operands[1]);
+ optype1 = OFFSOP;
+ }
+
+ /* If an operand is an unoffsettable memory ref, find a register
+ we can increment temporarily to make it refer to the second word. */
+
+ if (optype0 == MEMOP)
+ addreg0 = find_addr_reg (XEXP (operands[0], 0));
+
+ if (optype1 == MEMOP)
+ addreg1 = find_addr_reg (XEXP (operands[1], 0));
+
+ /* Ok, we can do one word at a time.
+ Normally we do the low-numbered word first,
+ but if either operand is autodecrementing then we
+ do the high-numbered word first.
+
+ In either case, set up in LATEHALF the operands to use
+ for the high-numbered word and in some cases alter the
+ operands in OPERANDS to be suitable for the low-numbered word. */
+
+ if (size == 12)
+ {
+ if (optype0 == REGOP)
+ {
+ 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);
+ }
+ else
+ {
+ middlehalf[0] = operands[0];
+ latehalf[0] = operands[0];
+ }
+
+ if (optype1 == REGOP)
+ {
+ 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);
+ }
+ else if (optype1 == CNSTOP)
+ {
+ if (GET_CODE (operands[1]) == CONST_DOUBLE)
+ {
+ REAL_VALUE_TYPE r;
+ long l[3];
+
+ REAL_VALUE_FROM_CONST_DOUBLE (r, operands[1]);
+ REAL_VALUE_TO_TARGET_LONG_DOUBLE (r, l);
+ operands[1] = GEN_INT (l[0]);
+ middlehalf[1] = GEN_INT (l[1]);
+ latehalf[1] = GEN_INT (l[2]);
+ }
+ else if (CONSTANT_P (operands[1]))
+ {
+ /* actually, no non-CONST_DOUBLE constant should ever
+ appear here. */
+ abort ();
+ if (GET_CODE (operands[1]) == CONST_INT && INTVAL (operands[1]) < 0)
+ latehalf[1] = constm1_rtx;
+ else
+ latehalf[1] = const0_rtx;
+ }
+ }
+ else
+ {
+ middlehalf[1] = operands[1];
+ latehalf[1] = operands[1];
+ }
+ }
+ else
+ /* size is not 12: */
+ {
+ if (optype0 == REGOP)
+ 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);
+ else if (optype1 == OFFSOP)
+ latehalf[1] = adj_offsettable_operand (operands[1], size - 4);
+ else if (optype1 == CNSTOP)
+ split_double (operands[1], &operands[1], &latehalf[1]);
+ else
+ latehalf[1] = operands[1];
+ }
+
+ /* If insn is effectively movd N(sp),-(sp) then we will do the
+ high word first. We should use the adjusted operand 1 (which is N+4(sp))
+ for the low word as well, to compensate for the first decrement of sp. */
+ if (optype0 == PUSHOP
+ && REGNO (XEXP (XEXP (operands[0], 0), 0)) == STACK_POINTER_REGNUM
+ && reg_overlap_mentioned_p (stack_pointer_rtx, operands[1]))
+ operands[1] = 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
+ emit the move late-half first. Otherwise, compute the MEM address
+ into the upper part of N and use that as a pointer to the memory
+ operand. */
+ if (optype0 == REGOP
+ && (optype1 == OFFSOP || optype1 == MEMOP))
+ {
+ 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.
+ 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 %a1,%0", xops);
+ 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);
+ }
+ else
+ {
+ operands[1] = gen_rtx_MEM (DImode, latehalf[0]);
+ latehalf[1] = adj_offsettable_operand (operands[1], size-4);
+ }
+ }
+ else if (size == 12
+ && reg_overlap_mentioned_p (middlehalf[0],
+ XEXP (operands[1], 0)))
+ {
+ /* 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 ();
+
+ /* 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);
+ return "";
+ }
+ 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 one or both operands autodecrementing,
+ do the two words, high-numbered first. */
+
+ /* Likewise, the first move would clobber the source of the second one,
+ do them in the other order. This happens only for registers;
+ such overlap can't happen in memory unless the user explicitly
+ sets it up, and that is an undefined circumstance. */
+
+ if (optype0 == PUSHOP || optype1 == PUSHOP
+ || (optype0 == REGOP && optype1 == REGOP
+ && ((middlehalf[1] && REGNO (operands[0]) == REGNO (middlehalf[1]))
+ || REGNO (operands[0]) == REGNO (latehalf[1])))
+ || dest_overlapped_low)
+ {
+ /* Make any unoffsettable addresses point at high-numbered word. */
+ if (addreg0)
+ {
+ if (size == 12)
+ output_asm_insn ("addq%.l %#8,%0", &addreg0);
+ else
+ output_asm_insn ("addq%.l %#4,%0", &addreg0);
+ }
+ if (addreg1)
+ {
+ if (size == 12)
+ output_asm_insn ("addq%.l %#8,%0", &addreg1);
+ else
+ output_asm_insn ("addq%.l %#4,%0", &addreg1);
+ }
+
+ /* Do that word. */
+ output_asm_insn (singlemove_string (latehalf), latehalf);
+
+ /* Undo the adds we just did. */
+ if (addreg0)
+ output_asm_insn ("subq%.l %#4,%0", &addreg0);
+ if (addreg1)
+ output_asm_insn ("subq%.l %#4,%0", &addreg1);
+
+ if (size == 12)
+ {
+ output_asm_insn (singlemove_string (middlehalf), middlehalf);
+ if (addreg0)
+ output_asm_insn ("subq%.l %#4,%0", &addreg0);
+ if (addreg1)
+ output_asm_insn ("subq%.l %#4,%0", &addreg1);
+ }
+
+ /* Do low-numbered word. */
+ return singlemove_string (operands);
+ }
+
+ /* Normal case: do the two words, low-numbered first. */
+
+ output_asm_insn (singlemove_string (operands), operands);
+
+ /* Do the middle one of the three words for long double */
+ if (size == 12)
+ {
+ if (addreg0)
+ output_asm_insn ("addq%.l %#4,%0", &addreg0);
+ if (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 ("addq%.l %#4,%0", &addreg0);
+ if (addreg1)
+ output_asm_insn ("addq%.l %#4,%0", &addreg1);
+
+ /* Do that word. */
+ output_asm_insn (singlemove_string (latehalf), latehalf);
+
+ /* Undo the adds we just did. */
+ if (addreg0)
+ {
+ if (size == 12)
+ output_asm_insn ("subq%.l %#8,%0", &addreg0);
+ else
+ output_asm_insn ("subq%.l %#4,%0", &addreg0);
+ }
+ if (addreg1)
+ {
+ if (size == 12)
+ output_asm_insn ("subq%.l %#8,%0", &addreg1);
+ else
+ output_asm_insn ("subq%.l %#4,%0", &addreg1);
+ }
+
+ return "";
+}
+
+/* Return a REG that occurs in ADDR with coefficient 1.
+ ADDR can be effectively incremented by incrementing REG. */
+
+static rtx
+find_addr_reg (addr)
+ rtx addr;
+{
+ while (GET_CODE (addr) == PLUS)
+ {
+ if (GET_CODE (XEXP (addr, 0)) == REG)
+ addr = XEXP (addr, 0);
+ else if (GET_CODE (XEXP (addr, 1)) == REG)
+ addr = XEXP (addr, 1);
+ else if (CONSTANT_P (XEXP (addr, 0)))
+ addr = XEXP (addr, 1);
+ else if (CONSTANT_P (XEXP (addr, 1)))
+ addr = XEXP (addr, 0);
+ else
+ abort ();
+ }
+ if (GET_CODE (addr) == REG)
+ return addr;
+ abort ();
+}
+
+/* Output assembler code to perform a 32 bit 3 operand add. */
+
+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";
+}
+
+/* Store in cc_status the expressions that the condition codes will
+ describe after execution of an instruction whose pattern is EXP.
+ Do not alter them if the instruction would not alter the cc's. */
+
+/* On the 68000, all the insns to store in an address register fail to
+ set the cc's. However, in some cases these instructions can make it
+ 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 (SET_SRC (exp)) == CALL)
+ {
+ CC_STATUS_INIT;
+ }
+ else if (ADDRESS_REG_P (SET_DEST (exp)))
+ {
+ if (cc_status.value1 && modified_in_p (cc_status.value1, insn))
+ cc_status.value1 = 0;
+ if (cc_status.value2 && modified_in_p (cc_status.value2, insn))
+ cc_status.value2 = 0;
+ }
+ else if (!FP_REG_P (SET_DEST (exp))
+ && SET_DEST (exp) != cc0_rtx
+ && (FP_REG_P (SET_SRC (exp))
+ || GET_CODE (SET_SRC (exp)) == FIX
+ || GET_CODE (SET_SRC (exp)) == FLOAT_TRUNCATE
+ || GET_CODE (SET_SRC (exp)) == FLOAT_EXTEND))
+ {
+ CC_STATUS_INIT;
+ }
+ /* A pair of move insns doesn't produce a useful overall cc. */
+ else if (!FP_REG_P (SET_DEST (exp))
+ && !FP_REG_P (SET_SRC (exp))
+ && GET_MODE_SIZE (GET_MODE (SET_SRC (exp))) > 4
+ && (GET_CODE (SET_SRC (exp)) == REG
+ || GET_CODE (SET_SRC (exp)) == MEM
+ || GET_CODE (SET_SRC (exp)) == CONST_DOUBLE))
+ {
+ CC_STATUS_INIT;
+ }
+ else if (GET_CODE (SET_SRC (exp)) == CALL)
+ {
+ CC_STATUS_INIT;
+ }
+ else if (XEXP (exp, 0) != pc_rtx)
+ {
+ cc_status.flags = 0;
+ cc_status.value1 = XEXP (exp, 0);
+ cc_status.value2 = XEXP (exp, 1);
+ }
+ }
+ else if (GET_CODE (exp) == PARALLEL
+ && GET_CODE (XVECEXP (exp, 0, 0)) == SET)
+ {
+ if (ADDRESS_REG_P (XEXP (XVECEXP (exp, 0, 0), 0)))
+ CC_STATUS_INIT;
+ else if (XEXP (XVECEXP (exp, 0, 0), 0) != pc_rtx)
+ {
+ cc_status.flags = 0;
+ cc_status.value1 = XEXP (XVECEXP (exp, 0, 0), 0);
+ cc_status.value2 = XEXP (XVECEXP (exp, 0, 0), 1);
+ }
+ }
+ else
+ CC_STATUS_INIT;
+ if (cc_status.value2 != 0
+ && 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)))
+ 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;
+ case ZERO_EXTEND:
+ /* (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. */
+ 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.flags = CC_IN_68881;
+}
+
+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]);
+
+ if (code != 0)
+ {
+ static char buf[40];
+
+ sprintf (buf, "fpmove%%.d %%%%%d,%%0", code & 0x1ff);
+ return buf;
+ }
+ return "fpmove%.d %1,%0";
+ }
+ else
+#endif
+ {
+ int code = standard_68881_constant_p (operands[1]);
+
+ if (code != 0)
+ {
+ static char buf[40];
+
+ sprintf (buf, "fmovecr %%#0x%x,%%0", code & 0xff);
+ return buf;
+ }
+ return "fmove%.d %1,%0";
+ }
+}
+
+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];
+
+ sprintf (buf, "fpmove%%.s %%%%%d,%%0", code & 0x1ff);
+ return buf;
+ }
+ return "fpmove%.s %1,%0";
+ }
+ else
+#endif /* defined SUPPORT_SUN_FPA */
+ {
+ int code = standard_68881_constant_p (operands[1]);
+
+ if (code != 0)
+ {
+ static char buf[40];
+
+ 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
+ from the "fmovecr" instruction.
+ 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. */
+
+static int inited_68881_table = 0;
+
+char *strings_68881[7] = {
+ "0.0",
+ "1.0",
+ "10.0",
+ "100.0",
+ "10000.0",
+ "1e8",
+ "1e16"
+ };
+
+int codes_68881[7] = {
+ 0x0f,
+ 0x32,
+ 0x33,
+ 0x34,
+ 0x35,
+ 0x36,
+ 0x37
+ };
+
+REAL_VALUE_TYPE values_68881[7];
+
+/* Set up values_68881 array by converting the decimal values
+ strings_68881 to binary. */
+
+void
+init_68881_table ()
+{
+ int i;
+ REAL_VALUE_TYPE r;
+ enum machine_mode mode;
+
+ mode = SFmode;
+ for (i = 0; i < 7; i++)
+ {
+ if (i == 6)
+ mode = DFmode;
+ r = REAL_VALUE_ATOF (strings_68881[i], mode);
+ values_68881[i] = r;
+ }
+ inited_68881_table = 1;
+}
+
+int
+standard_68881_constant_p (x)
+ rtx x;
+{
+ REAL_VALUE_TYPE r;
+ int i;
+
+#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
+#if HOST_FLOAT_FORMAT != TARGET_FLOAT_FORMAT
+ if (! flag_pretend_float)
+ return 0;
+#endif
+#endif
+
+ if (! inited_68881_table)
+ init_68881_table ();
+
+ REAL_VALUE_FROM_CONST_DOUBLE (r, x);
+
+ for (i = 0; i < 6; i++)
+ {
+ if (REAL_VALUES_EQUAL (r, values_68881[i]))
+ return (codes_68881[i]);
+ }
+
+ if (GET_MODE (x) == SFmode)
+ return 0;
+
+ if (REAL_VALUES_EQUAL (r, values_68881[6]))
+ return (codes_68881[6]);
+
+ /* larger powers of ten in the constants ram are not used
+ because they are not equal to a `double' C constant. */
+ return 0;
+}
+
+/* If X is a floating-point constant, return the logarithm of X base 2,
+ or 0 if X is not a power of 2. */
+
+int
+floating_exact_log2 (x)
+ 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
+
+ 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;
+}
+
+#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)
+ 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]);
+ }
+
+ 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;
+}
+#endif /* define SUPPORT_SUN_FPA */
+
+/* A C compound statement to output to stdio stream STREAM the
+ assembler syntax for an instruction operand X. X is an RTL
+ expression.
+
+ CODE is a value that can be used to specify one of several ways
+ of printing the operand. It is used when identical operands
+ must be printed differently depending on the context. CODE
+ comes from the `%' specification that was used to request
+ printing of the operand. If the specification was just `%DIGIT'
+ then CODE is 0; if the specification was `%LTR DIGIT' then CODE
+ is the ASCII code for LTR.
+
+ If X is a register, this macro should print the register's name.
+ The names can be found in an array `reg_names' whose type is
+ `char *[]'. `reg_names' is initialized from `REGISTER_NAMES'.
+
+ When the machine description has a specification `%PUNCT' (a `%'
+ followed by a punctuation character), this macro is called with
+ a null pointer for X and the punctuation character for CODE.
+
+ The m68k specific codes are:
+
+ '.' for dot needed in Motorola-style opcode names.
+ '-' for an operand pushing on the stack:
+ sp@-, -(sp) or -(%sp) depending on the style of syntax.
+ '+' for an operand pushing on the stack:
+ sp@+, (sp)+ or (%sp)+ depending on the style of syntax.
+ '@' 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, $ 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.
+ '/' for register prefix needed by longlong.h.
+
+ '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.
+ '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.
+
+ */
+
+void
+print_operand (file, op, letter)
+ FILE *file; /* file to write to */
+ rtx op; /* operand to print */
+ int letter; /* %<letter> or 0 */
+{
+#ifdef SUPPORT_SUN_FPA
+ int i;
+#endif
+
+ if (letter == '.')
+ {
+#if defined (MOTOROLA) && !defined (CRDS)
+ asm_fprintf (file, ".");
+#endif
+ }
+ else if (letter == '#')
+ {
+ asm_fprintf (file, "%0I");
+ }
+ else if (letter == '-')
+ {
+#ifdef MOTOROLA
+ asm_fprintf (file, "-(%Rsp)");
+#else
+ asm_fprintf (file, "%Rsp@-");
+#endif
+ }
+ else if (letter == '+')
+ {
+#ifdef MOTOROLA
+ asm_fprintf (file, "(%Rsp)+");
+#else
+ asm_fprintf (file, "%Rsp@+");
+#endif
+ }
+ else if (letter == '@')
+ {
+#ifdef MOTOROLA
+ asm_fprintf (file, "(%Rsp)");
+#else
+ asm_fprintf (file, "%Rsp@");
+#endif
+ }
+ else if (letter == '!')
+ {
+ asm_fprintf (file, "%Rfpcr");
+ }
+ else if (letter == '$')
+ {
+ if (TARGET_68040_ONLY)
+ {
+ fprintf (file, "s");
+ }
+ }
+ else if (letter == '&')
+ {
+ if (TARGET_68040_ONLY)
+ {
+ fprintf (file, "d");
+ }
+ }
+ else if (letter == '/')
+ {
+ asm_fprintf (file, "%R");
+ }
+ 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]);
+ }
+ else
+#endif
+ {
+ 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)
+ {
+ output_address (XEXP (op, 0));
+ if (letter == 'd' && ! TARGET_68020
+ && CONSTANT_ADDRESS_P (XEXP (op, 0))
+ && !(GET_CODE (XEXP (op, 0)) == CONST_INT
+ && 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;
+ REAL_VALUE_FROM_CONST_DOUBLE (r, op);
+ ASM_OUTPUT_FLOAT_OPERAND (letter, file, r);
+ }
+ else if (GET_CODE (op) == CONST_DOUBLE && GET_MODE (op) == XFmode)
+ {
+ REAL_VALUE_TYPE r;
+ REAL_VALUE_FROM_CONST_DOUBLE (r, op);
+ ASM_OUTPUT_LONG_DOUBLE_OPERAND (file, r);
+ }
+ else if (GET_CODE (op) == CONST_DOUBLE && GET_MODE (op) == DFmode)
+ {
+ REAL_VALUE_TYPE r;
+ REAL_VALUE_FROM_CONST_DOUBLE (r, op);
+ ASM_OUTPUT_DOUBLE_OPERAND (file, r);
+ }
+ else
+ {
+ asm_fprintf (file, "%0I"); output_addr_const (file, op);
+ }
+}
+
+
+/* A C compound statement to output to stdio stream STREAM the
+ assembler syntax for an instruction operand that is a memory
+ reference whose address is ADDR. ADDR is an RTL expression.
+
+ Note that this contains a kludge that knows that the only reason
+ we have an address (plus (label_ref...) (reg...)) when not generating
+ PIC code is in the insn before a tablejump, and we know that m68k.md
+ generates a label LInnn: on such an insn.
+
+ It is possible for PIC to generate a (plus (label_ref...) (reg...))
+ and we handle that just like we would a (plus (symbol_ref...) (reg...)).
+
+ Some SGS assemblers have a bug such that "Lnnn-LInnn-2.b(pc,d0.l*2)"
+ fails to assemble. Luckily "Lnnn(pc,d0.l*2)" produces the results
+ we want. This difference can be accommodated by using an assembler
+ 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.
+
+ This routine is responsible for distinguishing between -fpic and -fPIC
+ style relocations in an address. When generating -fpic code the
+ 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;
+ rtx addr;
+{
+ register rtx reg1, reg2, breg, ireg;
+ rtx offset;
+
+ switch (GET_CODE (addr))
+ {
+ case REG:
+#ifdef MOTOROLA
+ fprintf (file, "(%s)", reg_names[REGNO (addr)]);
+#else
+ fprintf (file, "%s@", reg_names[REGNO (addr)]);
+#endif
+ break;
+ case PRE_DEC:
+#ifdef MOTOROLA
+ fprintf (file, "-(%s)", reg_names[REGNO (XEXP (addr, 0))]);
+#else
+ fprintf (file, "%s@-", reg_names[REGNO (XEXP (addr, 0))]);
+#endif
+ break;
+ case POST_INC:
+#ifdef MOTOROLA
+ fprintf (file, "(%s)+", reg_names[REGNO (XEXP (addr, 0))]);
+#else
+ fprintf (file, "%s@+", reg_names[REGNO (XEXP (addr, 0))]);
+#endif
+ break;
+ case PLUS:
+ reg1 = reg2 = ireg = breg = offset = 0;
+ if (CONSTANT_ADDRESS_P (XEXP (addr, 0)))
+ {
+ offset = XEXP (addr, 0);
+ addr = XEXP (addr, 1);
+ }
+ else if (CONSTANT_ADDRESS_P (XEXP (addr, 1)))
+ {
+ offset = XEXP (addr, 1);
+ addr = XEXP (addr, 0);
+ }
+ if (GET_CODE (addr) != PLUS)
+ {
+ ;
+ }
+ else if (GET_CODE (XEXP (addr, 0)) == SIGN_EXTEND)
+ {
+ reg1 = XEXP (addr, 0);
+ addr = XEXP (addr, 1);
+ }
+ else if (GET_CODE (XEXP (addr, 1)) == SIGN_EXTEND)
+ {
+ reg1 = XEXP (addr, 1);
+ addr = XEXP (addr, 0);
+ }
+ else if (GET_CODE (XEXP (addr, 0)) == MULT)
+ {
+ reg1 = XEXP (addr, 0);
+ addr = XEXP (addr, 1);
+ }
+ else if (GET_CODE (XEXP (addr, 1)) == MULT)
+ {
+ reg1 = XEXP (addr, 1);
+ addr = XEXP (addr, 0);
+ }
+ else if (GET_CODE (XEXP (addr, 0)) == REG)
+ {
+ reg1 = XEXP (addr, 0);
+ addr = XEXP (addr, 1);
+ }
+ else if (GET_CODE (XEXP (addr, 1)) == REG)
+ {
+ reg1 = XEXP (addr, 1);
+ addr = XEXP (addr, 0);
+ }
+ if (GET_CODE (addr) == REG || GET_CODE (addr) == MULT
+ || GET_CODE (addr) == SIGN_EXTEND)
+ {
+ if (reg1 == 0)
+ {
+ reg1 = addr;
+ }
+ else
+ {
+ reg2 = addr;
+ }
+ addr = 0;
+ }
+#if 0 /* for OLD_INDEXING */
+ else if (GET_CODE (addr) == PLUS)
+ {
+ if (GET_CODE (XEXP (addr, 0)) == REG)
+ {
+ reg2 = XEXP (addr, 0);
+ addr = XEXP (addr, 1);
+ }
+ else if (GET_CODE (XEXP (addr, 1)) == REG)
+ {
+ reg2 = XEXP (addr, 1);
+ addr = XEXP (addr, 0);
+ }
+ }
+#endif
+ if (offset != 0)
+ {
+ if (addr != 0)
+ {
+ abort ();
+ }
+ addr = offset;
+ }
+ if ((reg1 && (GET_CODE (reg1) == SIGN_EXTEND
+ || GET_CODE (reg1) == MULT))
+ || (reg2 != 0 && REGNO_OK_FOR_BASE_P (REGNO (reg2))))
+ {
+ breg = reg2;
+ ireg = reg1;
+ }
+ else if (reg1 != 0 && REGNO_OK_FOR_BASE_P (REGNO (reg1)))
+ {
+ breg = reg1;
+ ireg = reg2;
+ }
+ if (ireg != 0 && breg == 0 && GET_CODE (addr) == LABEL_REF
+ && ! (flag_pic && ireg == pic_offset_table_rtx))
+ {
+ int scale = 1;
+ if (GET_CODE (ireg) == MULT)
+ {
+ scale = INTVAL (XEXP (ireg, 1));
+ ireg = XEXP (ireg, 0);
+ }
+ if (GET_CODE (ireg) == SIGN_EXTEND)
+ {
+ ASM_OUTPUT_CASE_FETCH (file,
+ CODE_LABEL_NUMBER (XEXP (addr, 0)),
+ reg_names[REGNO (XEXP (ireg, 0))]);
+ fprintf (file, "w");
+ }
+ else
+ {
+ ASM_OUTPUT_CASE_FETCH (file,
+ CODE_LABEL_NUMBER (XEXP (addr, 0)),
+ reg_names[REGNO (ireg)]);
+ fprintf (file, "l");
+ }
+ if (scale != 1)
+ {
+#ifdef MOTOROLA
+ fprintf (file, "*%d", scale);
+#else
+ fprintf (file, ":%d", scale);
+#endif
+ }
+ putc (')', file);
+ break;
+ }
+ if (breg != 0 && ireg == 0 && GET_CODE (addr) == LABEL_REF
+ && ! (flag_pic && breg == pic_offset_table_rtx))
+ {
+ ASM_OUTPUT_CASE_FETCH (file,
+ CODE_LABEL_NUMBER (XEXP (addr, 0)),
+ reg_names[REGNO (breg)]);
+ fprintf (file, "l)");
+ break;
+ }
+ if (ireg != 0 || breg != 0)
+ {
+ int scale = 1;
+ if (breg == 0)
+ {
+ abort ();
+ }
+ if (! flag_pic && addr && GET_CODE (addr) == LABEL_REF)
+ {
+ abort ();
+ }
+#ifdef MOTOROLA
+ if (addr != 0)
+ {
+ output_addr_const (file, addr);
+ if (flag_pic && (breg == pic_offset_table_rtx))
+ {
+ fprintf (file, "@GOT");
+ if (flag_pic == 1)
+ fprintf (file, ".w");
+ }
+ }
+ fprintf (file, "(%s", reg_names[REGNO (breg)]);
+ if (ireg != 0)
+ {
+ putc (',', file);
+ }
+#else
+ fprintf (file, "%s@(", reg_names[REGNO (breg)]);
+ if (addr != 0)
+ {
+ output_addr_const (file, addr);
+ if ((flag_pic == 1) && (breg == pic_offset_table_rtx))
+ fprintf (file, ":w");
+ if ((flag_pic == 2) && (breg == pic_offset_table_rtx))
+ fprintf (file, ":l");
+ }
+ if (addr != 0 && ireg != 0)
+ {
+ putc (',', file);
+ }
+#endif
+ if (ireg != 0 && GET_CODE (ireg) == MULT)
+ {
+ scale = INTVAL (XEXP (ireg, 1));
+ ireg = XEXP (ireg, 0);
+ }
+ if (ireg != 0 && GET_CODE (ireg) == SIGN_EXTEND)
+ {
+#ifdef MOTOROLA
+ fprintf (file, "%s.w", reg_names[REGNO (XEXP (ireg, 0))]);
+#else
+ fprintf (file, "%s:w", reg_names[REGNO (XEXP (ireg, 0))]);
+#endif
+ }
+ else if (ireg != 0)
+ {
+#ifdef MOTOROLA
+ fprintf (file, "%s.l", reg_names[REGNO (ireg)]);
+#else
+ fprintf (file, "%s:l", reg_names[REGNO (ireg)]);
+#endif
+ }
+ if (scale != 1)
+ {
+#ifdef MOTOROLA
+ fprintf (file, "*%d", scale);
+#else
+ fprintf (file, ":%d", scale);
+#endif
+ }
+ putc (')', file);
+ break;
+ }
+ else if (reg1 != 0 && GET_CODE (addr) == LABEL_REF
+ && ! (flag_pic && reg1 == pic_offset_table_rtx))
+ {
+ ASM_OUTPUT_CASE_FETCH (file,
+ CODE_LABEL_NUMBER (XEXP (addr, 0)),
+ reg_names[REGNO (reg1)]);
+ fprintf (file, "l)");
+ break;
+ }
+ /* FALL-THROUGH (is this really what we want? */
+ default:
+ if (GET_CODE (addr) == CONST_INT
+ && INTVAL (addr) < 0x8000
+ && INTVAL (addr) >= -0x8000)
+ {
+#ifdef MOTOROLA
+#ifdef SGS
+ /* Many SGS assemblers croak on size specifiers for constants. */
+ fprintf (file, "%d", INTVAL (addr));
+#else
+ fprintf (file, "%d.w", INTVAL (addr));
+#endif
+#else
+ fprintf (file, "%d:w", INTVAL (addr));
+#endif
+ }
+ else
+ {
+ output_addr_const (file, addr);
+ }
+ break;
+ }
+}
+
+/* Check for cases where a clr insns can be omitted from code using
+ strict_low_part sets. For example, the second clrl here is not needed:
+ clrl d0; movw a0@+,d0; use d0; clrl d0; movw a0@+; use d0; ...
+
+ MODE is the mode of this STRICT_LOW_PART set. FIRST_INSN is the clear
+ insn we are checking for redundancy. TARGET is the register set by the
+ clear insn. */
+
+int
+strict_low_part_peephole_ok (mode, first_insn, target)
+ enum machine_mode mode;
+ rtx first_insn;
+ rtx target;
+{
+ rtx p;
+
+ p = prev_nonnote_insn (first_insn);
+
+ while (p)
+ {
+ /* If it isn't an insn, then give up. */
+ if (GET_CODE (p) != INSN)
+ return 0;
+
+ if (reg_set_p (target, p))
+ {
+ rtx set = single_set (p);
+ rtx dest;
+
+ /* If it isn't an easy to recognize insn, then give up. */
+ if (! set)
+ return 0;
+
+ dest = SET_DEST (set);
+
+ /* If this sets the entire target register to zero, then our
+ first_insn is redundant. */
+ if (rtx_equal_p (dest, target)
+ && SET_SRC (set) == const0_rtx)
+ return 1;
+ else if (GET_CODE (dest) == STRICT_LOW_PART
+ && GET_CODE (XEXP (dest, 0)) == REG
+ && REGNO (XEXP (dest, 0)) == REGNO (target)
+ && (GET_MODE_SIZE (GET_MODE (XEXP (dest, 0)))
+ <= GET_MODE_SIZE (mode)))
+ /* This is a strict low part set which modifies less than
+ we are using, so it is safe. */
+ ;
+ else
+ return 0;
+ }
+
+ p = prev_nonnote_insn (p);
+
+ }
+
+ 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 && INTVAL (op) >= 0)
+ || (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));
+}
+
+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";
+}
+
+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";
+}
+
+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";
+}