aboutsummaryrefslogtreecommitdiff
path: root/gcc/config
diff options
context:
space:
mode:
authorKazu Hirata <kazu@codesourcery.com>2007-03-06 08:58:40 +0000
committerRichard Sandiford <rsandifo@gcc.gnu.org>2007-03-06 08:58:40 +0000
commita40ed0f3103b04e249f2ad78ae36e328f2d6f1cd (patch)
tree4ec6be1791e9c668d0c60ecc8d9f725a3d0d2609 /gcc/config
parentfc2241eb94b9b29bd2613ea02224a78fdb242a95 (diff)
downloadgcc-a40ed0f3103b04e249f2ad78ae36e328f2d6f1cd.zip
gcc-a40ed0f3103b04e249f2ad78ae36e328f2d6f1cd.tar.gz
gcc-a40ed0f3103b04e249f2ad78ae36e328f2d6f1cd.tar.bz2
200x-xx-xx Kazu Hirata <kazu@codesourcery.com> Richard Sandiford <richard@codesourcery.com>
gcc/ 200x-xx-xx Kazu Hirata <kazu@codesourcery.com> Richard Sandiford <richard@codesourcery.com> * config/m68k/m68k-protos.h (m68k_interrupt_function_p): Declare. (m68k_movem_pattern_p, m68k_output_movem): Likewise. (m68k_expand_prologue, m68k_expand_epilogue): Likewise. * config/m68k/m68k.h (EPILOGUE_USES): Define. Treat all registers as being live on exit from an interrupt function. (PRINT_OPERAND_PUNCT_VALID_P): Return true for '?'. * config/m68k/m68k.c (MIN_MOVEM_REGS, MIN_FMOVEM_REGS): New macros. (m68k_frame): Remove reg_rev_mask and fpu_rev_mask. (TARGET_ASM_FUNCTION_PROLOGUE, TARGET_ASM_FUNCTION_EPILOGUE): Delete. (m68k_interrupt_function_p): Globalize. (m68k_compute_frame_layout): Remove reverse mask code. (m68k_emit_movem, m68k_set_frame_related): New functions. (m68k_output_function_prologue): Delete in favor of... (m68k_expand_prologue): ...this new function. (m68k_output_function_epilogue): Delete in favor of... (m68k_expand_epilogue): ...this new function. (m68k_split_offset, m68k_movem_pattern_p, m68k_output_movem): New functions. (print_operand): Handle %?. * config/m68k/m68k.md (UNSPEC_SIN, UNSPEC_COS): Remove excess space. (UNSPEC_GOT, A1_REG, PIC_REG, FP0_REG): New constants. (prologue, epilogue): New patterns. (return): Turn into a define_expand. (*return): New pattern, derived from old "return" pattern. Use rte rather than rts for interrupt functions. Only use rtd if the pop count is nonzero. (*m68k_store_multiple, *m68k_store_multiple_automod): New patterns. (*m68k_load_multiple, *m68k_load_multiple_automod): Likewise. (link, *link, unlink, *unlink, load_got): Likewise. Co-Authored-By: Richard Sandiford <richard@codesourcery.com> From-SVN: r122605
Diffstat (limited to 'gcc/config')
-rw-r--r--gcc/config/m68k/m68k-protos.h5
-rw-r--r--gcc/config/m68k/m68k.c929
-rw-r--r--gcc/config/m68k/m68k.h7
-rw-r--r--gcc/config/m68k/m68k.md159
4 files changed, 645 insertions, 455 deletions
diff --git a/gcc/config/m68k/m68k-protos.h b/gcc/config/m68k/m68k-protos.h
index ba137d6..a6705c0 100644
--- a/gcc/config/m68k/m68k-protos.h
+++ b/gcc/config/m68k/m68k-protos.h
@@ -21,6 +21,7 @@ Boston, MA 02110-1301, USA. */
/* Define functions defined in aux-output.c and used in templates. */
#ifdef RTX_CODE
+extern bool m68k_interrupt_function_p (tree);
extern HOST_WIDE_INT m68k_initial_elimination_offset (int from, int to);
extern void split_di (rtx[], int, rtx[], rtx[]);
@@ -61,12 +62,16 @@ extern int valid_dbcc_comparison_p_2 (rtx, enum machine_mode);
extern rtx m68k_libcall_value (enum machine_mode);
extern rtx m68k_function_value (tree, tree);
extern int emit_move_sequence (rtx *, enum machine_mode, rtx);
+extern bool m68k_movem_pattern_p (rtx, rtx, HOST_WIDE_INT, bool);
+extern const char *m68k_output_movem (rtx *, rtx, HOST_WIDE_INT, bool);
#endif /* RTX_CODE */
extern bool m68k_regno_mode_ok (int, enum machine_mode);
extern int flags_in_68881 (void);
+extern void m68k_expand_prologue (void);
extern bool m68k_use_return_insn (void);
+extern void m68k_expand_epilogue (void);
extern void override_options (void);
extern const char *m68k_cpp_cpu_ident (const char *);
extern const char *m68k_cpp_cpu_family (const char *);
diff --git a/gcc/config/m68k/m68k.c b/gcc/config/m68k/m68k.c
index 57e15c8..1d6c035 100644
--- a/gcc/config/m68k/m68k.c
+++ b/gcc/config/m68k/m68k.c
@@ -70,6 +70,16 @@ enum reg_class regno_reg_class[] =
#endif
+/* The minimum number of integer registers that we want to save with the
+ movem instruction. Using two movel instructions instead of a single
+ moveml is about 15% faster for the 68020 and 68030 at no expense in
+ code size. */
+#define MIN_MOVEM_REGS 3
+
+/* The minimum number of floating point registers that we want to save
+ with the fmovem instruction. */
+#define MIN_FMOVEM_REGS 1
+
/* Structure describing stack frame layout. */
struct m68k_frame
{
@@ -85,12 +95,10 @@ struct m68k_frame
/* Data and address register. */
int reg_no;
unsigned int reg_mask;
- unsigned int reg_rev_mask;
/* FPU registers. */
int fpu_no;
unsigned int fpu_mask;
- unsigned int fpu_rev_mask;
/* Offsets relative to ARG_POINTER. */
HOST_WIDE_INT frame_pointer_offset;
@@ -127,15 +135,12 @@ struct m68k_address {
static bool m68k_handle_option (size_t, const char *, int);
static rtx find_addr_reg (rtx);
static const char *singlemove_string (rtx *);
-static void m68k_output_function_prologue (FILE *, HOST_WIDE_INT);
-static void m68k_output_function_epilogue (FILE *, HOST_WIDE_INT);
#ifdef M68K_TARGET_COFF
static void m68k_coff_asm_named_section (const char *, unsigned int, tree);
#endif /* M68K_TARGET_COFF */
static void m68k_output_mi_thunk (FILE *, tree, HOST_WIDE_INT,
HOST_WIDE_INT, tree);
static rtx m68k_struct_value_rtx (tree, int);
-static bool m68k_interrupt_function_p (tree func);
static tree m68k_handle_fndecl_attribute (tree *node, tree name,
tree args, int flags,
bool *no_add_attrs);
@@ -182,11 +187,6 @@ int m68k_last_compare_had_fp_operands;
#undef TARGET_ASM_UNALIGNED_SI_OP
#define TARGET_ASM_UNALIGNED_SI_OP TARGET_ASM_ALIGNED_SI_OP
-#undef TARGET_ASM_FUNCTION_PROLOGUE
-#define TARGET_ASM_FUNCTION_PROLOGUE m68k_output_function_prologue
-#undef TARGET_ASM_FUNCTION_EPILOGUE
-#define TARGET_ASM_FUNCTION_EPILOGUE m68k_output_function_epilogue
-
#undef TARGET_ASM_OUTPUT_MI_THUNK
#define TARGET_ASM_OUTPUT_MI_THUNK m68k_output_mi_thunk
#undef TARGET_ASM_CAN_OUTPUT_MI_THUNK
@@ -631,8 +631,8 @@ m68k_cpp_cpu_family (const char *prefix)
/* Return nonzero if FUNC is an interrupt function as specified by the
"interrupt_handler" attribute. */
-static bool
-m68k_interrupt_function_p(tree func)
+bool
+m68k_interrupt_function_p (tree func)
{
tree a;
@@ -665,7 +665,7 @@ static void
m68k_compute_frame_layout (void)
{
int regno, saved;
- unsigned int mask, rmask;
+ unsigned int mask;
bool interrupt_handler = m68k_interrupt_function_p (current_function_decl);
/* Only compute the frame once per function.
@@ -676,28 +676,25 @@ m68k_compute_frame_layout (void)
current_frame.size = (get_frame_size () + 3) & -4;
- mask = rmask = saved = 0;
+ mask = saved = 0;
for (regno = 0; regno < 16; regno++)
if (m68k_save_reg (regno, interrupt_handler))
{
- mask |= 1 << regno;
- rmask |= 1 << (15 - regno);
+ mask |= 1 << (regno - D0_REG);
saved++;
}
current_frame.offset = saved * 4;
current_frame.reg_no = saved;
current_frame.reg_mask = mask;
- current_frame.reg_rev_mask = rmask;
current_frame.foffset = 0;
- mask = rmask = saved = 0;
+ mask = saved = 0;
if (TARGET_HARD_FLOAT)
{
for (regno = 16; regno < 24; regno++)
if (m68k_save_reg (regno, interrupt_handler))
{
- mask |= 1 << (regno - 16);
- rmask |= 1 << (23 - regno);
+ mask |= 1 << (regno - FP0_REG);
saved++;
}
current_frame.foffset = saved * TARGET_FP_REG_SIZE;
@@ -705,7 +702,6 @@ m68k_compute_frame_layout (void)
}
current_frame.fpu_no = saved;
current_frame.fpu_mask = mask;
- current_frame.fpu_rev_mask = rmask;
/* Remember what function this frame refers to. */
current_frame.funcdef_no = current_function_funcdef_no;
@@ -793,154 +789,158 @@ m68k_save_reg (unsigned int regno, bool interrupt_handler)
return !call_used_regs[regno];
}
-/* 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. */
+/* Emit RTL for a MOVEM or FMOVEM instruction. BASE + OFFSET represents
+ the lowest memory address. COUNT is the number of registers to be
+ moved, with register REGNO + I being moved if bit I of MASK is set.
+ STORE_P specifies the direction of the move and ADJUST_STACK_P says
+ whether or not this is pre-decrement (if STORE_P) or post-increment
+ (if !STORE_P) operation. */
+
+static rtx
+m68k_emit_movem (rtx base, HOST_WIDE_INT offset,
+ unsigned int count, unsigned int regno,
+ unsigned int mask, bool store_p, bool adjust_stack_p)
+{
+ int i;
+ rtx body, addr, src, operands[2];
+ enum machine_mode mode;
+
+ body = gen_rtx_PARALLEL (VOIDmode, rtvec_alloc (adjust_stack_p + count));
+ mode = reg_raw_mode[regno];
+ i = 0;
+
+ if (adjust_stack_p)
+ {
+ src = plus_constant (base, (count
+ * GET_MODE_SIZE (mode)
+ * (HOST_WIDE_INT) (store_p ? -1 : 1)));
+ XVECEXP (body, 0, i++) = gen_rtx_SET (VOIDmode, base, src);
+ }
+
+ for (; mask != 0; mask >>= 1, regno++)
+ if (mask & 1)
+ {
+ addr = plus_constant (base, offset);
+ operands[!store_p] = gen_frame_mem (mode, addr);
+ operands[store_p] = gen_rtx_REG (mode, regno);
+ XVECEXP (body, 0, i++)
+ = gen_rtx_SET (VOIDmode, operands[0], operands[1]);
+ offset += GET_MODE_SIZE (mode);
+ }
+ gcc_assert (i == XVECLEN (body, 0));
+
+ return emit_insn (body);
+}
+
+/* Make INSN a frame-related instruction. */
static void
-m68k_output_function_prologue (FILE *stream,
- HOST_WIDE_INT size ATTRIBUTE_UNUSED)
+m68k_set_frame_related (rtx insn)
+{
+ rtx body;
+ int i;
+
+ RTX_FRAME_RELATED_P (insn) = 1;
+ body = PATTERN (insn);
+ if (GET_CODE (body) == PARALLEL)
+ for (i = 0; i < XVECLEN (body, 0); i++)
+ RTX_FRAME_RELATED_P (XVECEXP (body, 0, i)) = 1;
+}
+
+/* Emit RTL for the "prologue" define_expand. */
+
+void
+m68k_expand_prologue (void)
{
HOST_WIDE_INT fsize_with_regs;
- HOST_WIDE_INT cfa_offset = INCOMING_FRAME_SP_OFFSET;
+ rtx limit, src, dest, insn;
- m68k_compute_frame_layout();
+ m68k_compute_frame_layout ();
/* 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)
- asm_fprintf (stream, "\tcmp" ASM_DOT "l %I%s+%wd,%Rsp\n\ttrapcs\n",
- XSTR (stack_limit_rtx, 0), current_frame.size + 4);
+ {
+ limit = plus_constant (stack_limit_rtx, current_frame.size + 4);
+ if (!LEGITIMATE_CONSTANT_P (limit))
+ {
+ emit_move_insn (gen_rtx_REG (Pmode, D0_REG), limit);
+ limit = gen_rtx_REG (Pmode, D0_REG);
+ }
+ emit_insn (gen_cmpsi (stack_pointer_rtx, limit));
+ emit_insn (gen_conditional_trap (gen_rtx_LTU (VOIDmode,
+ cc0_rtx, const0_rtx),
+ const1_rtx));
+ }
- /* On ColdFire add register save into initial stack frame setup, if possible. */
fsize_with_regs = current_frame.size;
if (TARGET_COLDFIRE)
{
- if (current_frame.reg_no > 2)
- fsize_with_regs += current_frame.reg_no * 4;
- if (current_frame.fpu_no)
- fsize_with_regs += current_frame.fpu_no * 8;
+ /* ColdFire's move multiple instructions do not allow pre-decrement
+ addressing. Add the size of movem saves to the initial stack
+ allocation instead. */
+ if (current_frame.reg_no >= MIN_MOVEM_REGS)
+ fsize_with_regs += current_frame.reg_no * GET_MODE_SIZE (SImode);
+ if (current_frame.fpu_no >= MIN_FMOVEM_REGS)
+ fsize_with_regs += current_frame.fpu_no * GET_MODE_SIZE (DFmode);
}
if (frame_pointer_needed)
{
- if (current_frame.size == 0 && TUNE_68040)
- /* on the 68040, pea + move is faster than link.w 0 */
- fprintf (stream, (MOTOROLA
- ? "\tpea (%s)\n\tmove.l %s,%s\n"
- : "\tpea %s@\n\tmovel %s,%s\n"),
- M68K_REGNAME (FRAME_POINTER_REGNUM),
- M68K_REGNAME (STACK_POINTER_REGNUM),
- M68K_REGNAME (FRAME_POINTER_REGNUM));
- else if (fsize_with_regs < 0x8000)
- asm_fprintf (stream, "\tlink" ASM_DOTW " %s,%I%wd\n",
- M68K_REGNAME (FRAME_POINTER_REGNUM), -fsize_with_regs);
- else if (TARGET_68020)
- asm_fprintf (stream, "\tlink" ASM_DOTL " %s,%I%wd\n",
- M68K_REGNAME (FRAME_POINTER_REGNUM), -fsize_with_regs);
- else
- /* Adding negative number is faster on the 68040. */
- asm_fprintf (stream,
- "\tlink" ASM_DOTW " %s,%I0\n"
- "\tadd" ASM_DOT "l %I%wd,%Rsp\n",
- M68K_REGNAME (FRAME_POINTER_REGNUM), -fsize_with_regs);
- }
- else if (fsize_with_regs) /* !frame_pointer_needed */
- {
- if (fsize_with_regs < 0x8000)
+ if (fsize_with_regs == 0 && TUNE_68040)
{
- if (fsize_with_regs <= 8)
- {
- if (!TARGET_COLDFIRE)
- asm_fprintf (stream, "\tsubq" ASM_DOT "w %I%wd,%Rsp\n",
- fsize_with_regs);
- else
- asm_fprintf (stream, "\tsubq" ASM_DOT "l %I%wd,%Rsp\n",
- fsize_with_regs);
- }
- else if (fsize_with_regs <= 16 && TUNE_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 (stream,
- "\tsubq" ASM_DOT "w %I8,%Rsp\n"
- "\tsubq" ASM_DOT "w %I%wd,%Rsp\n",
- fsize_with_regs - 8);
- else if (TUNE_68040)
- /* Adding negative number is faster on the 68040. */
- asm_fprintf (stream, "\tadd" ASM_DOT "w %I%wd,%Rsp\n",
- -fsize_with_regs);
- else
- asm_fprintf (stream, (MOTOROLA
- ? "\tlea (%wd,%Rsp),%Rsp\n"
- : "\tlea %Rsp@(%wd),%Rsp\n"),
- -fsize_with_regs);
+ /* On the 68040, two separate moves are faster than link.w 0. */
+ dest = gen_frame_mem (Pmode,
+ gen_rtx_PRE_DEC (Pmode, stack_pointer_rtx));
+ m68k_set_frame_related (emit_move_insn (dest, frame_pointer_rtx));
+ m68k_set_frame_related (emit_move_insn (frame_pointer_rtx,
+ stack_pointer_rtx));
}
- else /* fsize_with_regs >= 0x8000 */
- asm_fprintf (stream, "\tadd" ASM_DOT "l %I%wd,%Rsp\n",
- -fsize_with_regs);
- } /* !frame_pointer_needed */
-
- if (dwarf2out_do_frame ())
- {
- if (frame_pointer_needed)
- {
- char *l;
- l = (char *) dwarf2out_cfi_label ();
- cfa_offset += 4;
- dwarf2out_reg_save (l, FRAME_POINTER_REGNUM, -cfa_offset);
- dwarf2out_def_cfa (l, FRAME_POINTER_REGNUM, cfa_offset);
- cfa_offset += current_frame.size;
- }
+ else if (fsize_with_regs < 0x8000 || TARGET_68020)
+ m68k_set_frame_related
+ (emit_insn (gen_link (frame_pointer_rtx,
+ GEN_INT (-4 - fsize_with_regs))));
else
- {
- cfa_offset += current_frame.size;
- dwarf2out_def_cfa ("", STACK_POINTER_REGNUM, cfa_offset);
- }
+ {
+ m68k_set_frame_related
+ (emit_insn (gen_link (frame_pointer_rtx, GEN_INT (-4))));
+ m68k_set_frame_related
+ (emit_insn (gen_addsi3 (stack_pointer_rtx,
+ stack_pointer_rtx,
+ GEN_INT (-fsize_with_regs))));
+ }
}
+ else if (fsize_with_regs != 0)
+ m68k_set_frame_related
+ (emit_insn (gen_addsi3 (stack_pointer_rtx,
+ stack_pointer_rtx,
+ GEN_INT (-fsize_with_regs))));
if (current_frame.fpu_mask)
{
+ gcc_assert (current_frame.fpu_no >= MIN_FMOVEM_REGS);
if (TARGET_68881)
- {
- asm_fprintf (stream, (MOTOROLA
- ? "\tfmovm %I0x%x,-(%Rsp)\n"
- : "\tfmovem %I0x%x,%Rsp@-\n"),
- current_frame.fpu_mask);
- }
+ m68k_set_frame_related
+ (m68k_emit_movem (stack_pointer_rtx,
+ current_frame.fpu_no * -GET_MODE_SIZE (XFmode),
+ current_frame.fpu_no, FP0_REG,
+ current_frame.fpu_mask, true, true));
else
{
int offset;
- /* stack already has registers in it. Find the offset from
- the bottom of stack to where the FP registers go */
- if (current_frame.reg_no <= 2)
+ /* If we're using moveml to save the integer registers,
+ the stack pointer will point to the bottom of the moveml
+ save area. Find the stack offset of the first FP register. */
+ if (current_frame.reg_no < MIN_MOVEM_REGS)
offset = 0;
else
- offset = current_frame.reg_no * 4;
- if (offset)
- asm_fprintf (stream,
- "\tfmovem %I0x%x,%d(%Rsp)\n",
- current_frame.fpu_rev_mask,
- offset);
- else
- asm_fprintf (stream,
- "\tfmovem %I0x%x,(%Rsp)\n",
- current_frame.fpu_rev_mask);
- }
-
- if (dwarf2out_do_frame ())
- {
- char *l = (char *) dwarf2out_cfi_label ();
- int n_regs, regno;
-
- cfa_offset += current_frame.fpu_no * TARGET_FP_REG_SIZE;
- if (! frame_pointer_needed)
- dwarf2out_def_cfa (l, STACK_POINTER_REGNUM, cfa_offset);
- for (regno = 16, n_regs = 0; regno < 24; regno++)
- if (current_frame.fpu_mask & (1 << (regno - 16)))
- dwarf2out_reg_save (l, regno, -cfa_offset
- + n_regs++ * TARGET_FP_REG_SIZE);
+ offset = current_frame.reg_no * GET_MODE_SIZE (SImode);
+ m68k_set_frame_related
+ (m68k_emit_movem (stack_pointer_rtx, offset,
+ current_frame.fpu_no, FP0_REG,
+ current_frame.fpu_mask, true, false));
}
}
@@ -949,96 +949,56 @@ m68k_output_function_prologue (FILE *stream,
if (current_function_limit_stack)
{
if (REG_P (stack_limit_rtx))
- asm_fprintf (stream, "\tcmp" ASM_DOT "l %s,%Rsp\n\ttrapcs\n",
- M68K_REGNAME (REGNO (stack_limit_rtx)));
+ {
+ emit_insn (gen_cmpsi (stack_pointer_rtx, stack_limit_rtx));
+ emit_insn (gen_conditional_trap (gen_rtx_LTU (VOIDmode,
+ cc0_rtx, const0_rtx),
+ const1_rtx));
+ }
else if (GET_CODE (stack_limit_rtx) != SYMBOL_REF)
warning (0, "stack limit expression is not supported");
}
- if (current_frame.reg_no <= 2)
+ if (current_frame.reg_no < MIN_MOVEM_REGS)
{
- /* 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. */
-
+ /* Store each register separately in the same order moveml does. */
int i;
- for (i = 0; i < 16; i++)
- if (current_frame.reg_rev_mask & (1 << i))
+ for (i = 16; i-- > 0; )
+ if (current_frame.reg_mask & (1 << i))
{
- asm_fprintf (stream, (MOTOROLA
- ? "\t%Omove.l %s,-(%Rsp)\n"
- : "\tmovel %s,%Rsp@-\n"),
- M68K_REGNAME (15 - i));
- if (dwarf2out_do_frame ())
- {
- char *l = (char *) dwarf2out_cfi_label ();
-
- cfa_offset += 4;
- if (! frame_pointer_needed)
- dwarf2out_def_cfa (l, STACK_POINTER_REGNUM, cfa_offset);
- dwarf2out_reg_save (l, 15 - i, -cfa_offset);
- }
+ src = gen_rtx_REG (SImode, D0_REG + i);
+ dest = gen_frame_mem (SImode,
+ gen_rtx_PRE_DEC (Pmode, stack_pointer_rtx));
+ m68k_set_frame_related (emit_insn (gen_movsi (dest, src)));
}
}
- else if (current_frame.reg_rev_mask)
+ else
{
if (TARGET_COLDFIRE)
- /* The ColdFire does not support the predecrement form of the
- MOVEM instruction, so we must adjust the stack pointer and
- then use the plain address register indirect mode.
- The required register save space was combined earlier with
- the fsize_with_regs amount. */
-
- asm_fprintf (stream, (MOTOROLA
- ? "\tmovm.l %I0x%x,(%Rsp)\n"
- : "\tmoveml %I0x%x,%Rsp@\n"),
- current_frame.reg_mask);
+ /* The required register save space has already been allocated.
+ The first register should be stored at (%sp). */
+ m68k_set_frame_related
+ (m68k_emit_movem (stack_pointer_rtx, 0,
+ current_frame.reg_no, D0_REG,
+ current_frame.reg_mask, true, false));
else
- asm_fprintf (stream, (MOTOROLA
- ? "\tmovm.l %I0x%x,-(%Rsp)\n"
- : "\tmoveml %I0x%x,%Rsp@-\n"),
- current_frame.reg_rev_mask);
- if (dwarf2out_do_frame ())
- {
- char *l = (char *) dwarf2out_cfi_label ();
- int n_regs, regno;
-
- cfa_offset += current_frame.reg_no * 4;
- if (! frame_pointer_needed)
- dwarf2out_def_cfa (l, STACK_POINTER_REGNUM, cfa_offset);
- for (regno = 0, n_regs = 0; regno < 16; regno++)
- if (current_frame.reg_mask & (1 << regno))
- dwarf2out_reg_save (l, regno, -cfa_offset + n_regs++ * 4);
- }
+ m68k_set_frame_related
+ (m68k_emit_movem (stack_pointer_rtx,
+ current_frame.reg_no * -GET_MODE_SIZE (SImode),
+ current_frame.reg_no, D0_REG,
+ current_frame.reg_mask, true, true));
}
- if (!TARGET_SEP_DATA && flag_pic
+
+ if (flag_pic
+ && !TARGET_SEP_DATA
&& (current_function_uses_pic_offset_table
|| (!current_function_is_leaf && TARGET_ID_SHARED_LIBRARY)))
{
- if (TARGET_ID_SHARED_LIBRARY)
- {
- asm_fprintf (stream, "\tmovel %s@(%s), %s\n",
- M68K_REGNAME (PIC_OFFSET_TABLE_REGNUM),
- m68k_library_id_string,
- M68K_REGNAME (PIC_OFFSET_TABLE_REGNUM));
- }
- else
- {
- if (MOTOROLA)
- asm_fprintf (stream,
- "\t%Olea (%Rpc, %U_GLOBAL_OFFSET_TABLE_@GOTPC), %s\n",
- M68K_REGNAME (PIC_OFFSET_TABLE_REGNUM));
- else
- {
- asm_fprintf (stream, "\tmovel %I%U_GLOBAL_OFFSET_TABLE_, %s\n",
- M68K_REGNAME (PIC_OFFSET_TABLE_REGNUM));
- asm_fprintf (stream, "\tlea %Rpc@(0,%s:l),%s\n",
- M68K_REGNAME (PIC_OFFSET_TABLE_REGNUM),
- M68K_REGNAME (PIC_OFFSET_TABLE_REGNUM));
- }
- }
+ insn = emit_insn (gen_load_got (pic_offset_table_rtx));
+ REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_MAYBE_DEAD,
+ const0_rtx,
+ REG_NOTES (insn));
}
}
@@ -1055,301 +1015,173 @@ m68k_use_return_insn (void)
return current_frame.offset == 0;
}
-/* This function generates the assembly code for function exit,
- on machines that need it.
+/* Emit RTL for the "epilogue" define_expand.
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. */
-static void
-m68k_output_function_epilogue (FILE *stream,
- HOST_WIDE_INT size ATTRIBUTE_UNUSED)
+void
+m68k_expand_epilogue (void)
{
HOST_WIDE_INT fsize, fsize_with_regs;
- bool big = false;
- bool restore_from_sp = false;
- rtx insn = get_last_insn ();
+ bool big, restore_from_sp;
m68k_compute_frame_layout ();
- /* 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)
- return;
-
fsize = current_frame.size;
+ big = false;
+ restore_from_sp = false;
- /* FIXME: leaf_function_p below is too strong.
+ /* FIXME : current_function_is_leaf 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 ()));
+ restore_from_sp = (!frame_pointer_needed
+ || (!current_function_calls_alloca
+ && current_function_is_leaf));
/* fsize_with_regs is the size we need to adjust the sp when
popping the frame. */
fsize_with_regs = fsize;
-
- /* Because the ColdFire doesn't support moveml with
- complex address modes, we must adjust the stack manually
- after restoring registers. When the frame pointer isn't used,
- we can merge movem adjustment into frame unlinking
- made immediately after it. */
if (TARGET_COLDFIRE && restore_from_sp)
{
- if (current_frame.reg_no > 2)
- fsize_with_regs += current_frame.reg_no * 4;
- if (current_frame.fpu_no)
- fsize_with_regs += current_frame.fpu_no * 8;
+ /* ColdFire's move multiple instructions do not allow post-increment
+ addressing. Add the size of movem loads to the final deallocation
+ instead. */
+ if (current_frame.reg_no >= MIN_MOVEM_REGS)
+ fsize_with_regs += current_frame.reg_no * GET_MODE_SIZE (SImode);
+ if (current_frame.fpu_no >= MIN_FMOVEM_REGS)
+ fsize_with_regs += current_frame.fpu_no * GET_MODE_SIZE (DFmode);
}
if (current_frame.offset + fsize >= 0x8000
- && ! restore_from_sp
+ && !restore_from_sp
&& (current_frame.reg_mask || current_frame.fpu_mask))
{
- /* Because the ColdFire doesn't support moveml with
- complex address modes we make an extra correction here. */
- if (TARGET_COLDFIRE)
- fsize += current_frame.offset;
-
- asm_fprintf (stream, "\t%Omove" ASM_DOT "l %I%wd,%Ra1\n", -fsize);
- fsize = 0, big = true;
+ if (TARGET_COLDFIRE
+ && (current_frame.reg_no >= MIN_MOVEM_REGS
+ || current_frame.fpu_no >= MIN_FMOVEM_REGS))
+ {
+ /* ColdFire's move multiple instructions do not support the
+ (d8,Ax,Xi) addressing mode, so we're as well using a normal
+ stack-based restore. */
+ emit_move_insn (gen_rtx_REG (Pmode, A1_REG),
+ GEN_INT (-(current_frame.offset + fsize)));
+ emit_insn (gen_addsi3 (stack_pointer_rtx,
+ gen_rtx_REG (Pmode, A1_REG),
+ frame_pointer_rtx));
+ restore_from_sp = true;
+ }
+ else
+ {
+ emit_move_insn (gen_rtx_REG (Pmode, A1_REG), GEN_INT (-fsize));
+ fsize = 0;
+ big = true;
+ }
}
- if (current_frame.reg_no <= 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. */
+ if (current_frame.reg_no < MIN_MOVEM_REGS)
+ {
+ /* Restore each register separately in the same order moveml does. */
int i;
- HOST_WIDE_INT offset = current_frame.offset + fsize;
+ HOST_WIDE_INT offset;
+ offset = current_frame.offset + fsize;
for (i = 0; i < 16; i++)
if (current_frame.reg_mask & (1 << i))
{
- if (big)
- {
- if (MOTOROLA)
- asm_fprintf (stream, "\t%Omove.l -%wd(%s,%Ra1.l),%s\n",
- offset,
- M68K_REGNAME (FRAME_POINTER_REGNUM),
- M68K_REGNAME (i));
- else
- asm_fprintf (stream, "\tmovel %s@(-%wd,%Ra1:l),%s\n",
- M68K_REGNAME (FRAME_POINTER_REGNUM),
- offset,
- M68K_REGNAME (i));
- }
- else if (restore_from_sp)
- asm_fprintf (stream, (MOTOROLA
- ? "\t%Omove.l (%Rsp)+,%s\n"
- : "\tmovel %Rsp@+,%s\n"),
- M68K_REGNAME (i));
- else
+ rtx addr;
+
+ if (big)
{
- if (MOTOROLA)
- asm_fprintf (stream, "\t%Omove.l -%wd(%s),%s\n",
- offset,
- M68K_REGNAME (FRAME_POINTER_REGNUM),
- M68K_REGNAME (i));
- else
- asm_fprintf (stream, "\tmovel %s@(-%wd),%s\n",
- M68K_REGNAME (FRAME_POINTER_REGNUM),
- offset,
- M68K_REGNAME (i));
+ /* Generate the address -OFFSET(%fp,%a1.l). */
+ addr = gen_rtx_REG (Pmode, A1_REG);
+ addr = gen_rtx_PLUS (Pmode, addr, frame_pointer_rtx);
+ addr = plus_constant (addr, -offset);
}
- offset -= 4;
- }
+ else if (restore_from_sp)
+ addr = gen_rtx_POST_INC (Pmode, stack_pointer_rtx);
+ else
+ addr = plus_constant (frame_pointer_rtx, -offset);
+ emit_move_insn (gen_rtx_REG (SImode, D0_REG + i),
+ gen_frame_mem (SImode, addr));
+ offset -= GET_MODE_SIZE (SImode);
+ }
}
else if (current_frame.reg_mask)
{
- /* The ColdFire requires special handling due to its limited moveml
- insn. */
- if (TARGET_COLDFIRE)
- {
- if (big)
- {
- asm_fprintf (stream, "\tadd" ASM_DOT "l %s,%Ra1\n",
- M68K_REGNAME (FRAME_POINTER_REGNUM));
- asm_fprintf (stream, (MOTOROLA
- ? "\tmovm.l (%Ra1),%I0x%x\n"
- : "\tmoveml %Ra1@,%I0x%x\n"),
- current_frame.reg_mask);
- }
- else if (restore_from_sp)
- asm_fprintf (stream, (MOTOROLA
- ? "\tmovm.l (%Rsp),%I0x%x\n"
- : "\tmoveml %Rsp@,%I0x%x\n"),
- current_frame.reg_mask);
- else
- {
- if (MOTOROLA)
- asm_fprintf (stream, "\tmovm.l -%wd(%s),%I0x%x\n",
- current_frame.offset + fsize,
- M68K_REGNAME (FRAME_POINTER_REGNUM),
- current_frame.reg_mask);
- else
- asm_fprintf (stream, "\tmoveml %s@(-%wd),%I0x%x\n",
- M68K_REGNAME (FRAME_POINTER_REGNUM),
- current_frame.offset + fsize,
- current_frame.reg_mask);
- }
- }
- else /* !TARGET_COLDFIRE */
- {
- if (big)
- {
- if (MOTOROLA)
- asm_fprintf (stream, "\tmovm.l -%wd(%s,%Ra1.l),%I0x%x\n",
- current_frame.offset + fsize,
- M68K_REGNAME (FRAME_POINTER_REGNUM),
- current_frame.reg_mask);
- else
- asm_fprintf (stream, "\tmoveml %s@(-%wd,%Ra1:l),%I0x%x\n",
- M68K_REGNAME (FRAME_POINTER_REGNUM),
- current_frame.offset + fsize,
- current_frame.reg_mask);
- }
- else if (restore_from_sp)
- {
- asm_fprintf (stream, (MOTOROLA
- ? "\tmovm.l (%Rsp)+,%I0x%x\n"
- : "\tmoveml %Rsp@+,%I0x%x\n"),
- current_frame.reg_mask);
- }
- else
- {
- if (MOTOROLA)
- asm_fprintf (stream, "\tmovm.l -%wd(%s),%I0x%x\n",
- current_frame.offset + fsize,
- M68K_REGNAME (FRAME_POINTER_REGNUM),
- current_frame.reg_mask);
- else
- asm_fprintf (stream, "\tmoveml %s@(-%wd),%I0x%x\n",
- M68K_REGNAME (FRAME_POINTER_REGNUM),
- current_frame.offset + fsize,
- current_frame.reg_mask);
- }
- }
+ if (big)
+ m68k_emit_movem (gen_rtx_PLUS (Pmode,
+ gen_rtx_REG (Pmode, A1_REG),
+ frame_pointer_rtx),
+ -(current_frame.offset + fsize),
+ current_frame.reg_no, D0_REG,
+ current_frame.reg_mask, false, false);
+ else if (restore_from_sp)
+ m68k_emit_movem (stack_pointer_rtx, 0,
+ current_frame.reg_no, D0_REG,
+ current_frame.reg_mask, false,
+ !TARGET_COLDFIRE);
+ else
+ m68k_emit_movem (frame_pointer_rtx,
+ -(current_frame.offset + fsize),
+ current_frame.reg_no, D0_REG,
+ current_frame.reg_mask, false, false);
}
- if (current_frame.fpu_rev_mask)
+
+ if (current_frame.fpu_no > 0)
{
if (big)
- {
- if (TARGET_COLDFIRE)
- {
- if (current_frame.reg_no)
- asm_fprintf (stream, MOTOROLA ?
- "\tfmovem.d %d(%Ra1),%I0x%x\n" :
- "\tfmovmd (%d,%Ra1),%I0x%x\n",
- current_frame.reg_no * 4,
- current_frame.fpu_rev_mask);
- else
- asm_fprintf (stream, MOTOROLA ?
- "\tfmovem.d (%Ra1),%I0x%x\n" :
- "\tfmovmd (%Ra1),%I0x%x\n",
- current_frame.fpu_rev_mask);
- }
- else if (MOTOROLA)
- asm_fprintf (stream, "\tfmovm -%wd(%s,%Ra1.l),%I0x%x\n",
- current_frame.foffset + fsize,
- M68K_REGNAME (FRAME_POINTER_REGNUM),
- current_frame.fpu_rev_mask);
- else
- asm_fprintf (stream, "\tfmovem %s@(-%wd,%Ra1:l),%I0x%x\n",
- M68K_REGNAME (FRAME_POINTER_REGNUM),
- current_frame.foffset + fsize,
- current_frame.fpu_rev_mask);
- }
+ m68k_emit_movem (gen_rtx_PLUS (Pmode,
+ gen_rtx_REG (Pmode, A1_REG),
+ frame_pointer_rtx),
+ -(current_frame.foffset + fsize),
+ current_frame.fpu_no, FP0_REG,
+ current_frame.fpu_mask, false, false);
else if (restore_from_sp)
{
if (TARGET_COLDFIRE)
{
int offset;
- /* Stack already has registers in it. Find the offset from
- the bottom of stack to where the FP registers go. */
- if (current_frame.reg_no <= 2)
+ /* If we used moveml to restore the integer registers, the
+ stack pointer will still point to the bottom of the moveml
+ save area. Find the stack offset of the first FP
+ register. */
+ if (current_frame.reg_no < MIN_MOVEM_REGS)
offset = 0;
else
- offset = current_frame.reg_no * 4;
- if (offset)
- asm_fprintf (stream,
- "\tfmovem %Rsp@(%d), %I0x%x\n",
- offset, current_frame.fpu_rev_mask);
- else
- asm_fprintf (stream,
- "\tfmovem %Rsp@, %I0x%x\n",
- current_frame.fpu_rev_mask);
+ offset = current_frame.reg_no * GET_MODE_SIZE (SImode);
+ m68k_emit_movem (stack_pointer_rtx, offset,
+ current_frame.fpu_no, FP0_REG,
+ current_frame.fpu_mask, false, false);
}
else
- asm_fprintf (stream, MOTOROLA ?
- "\tfmovm (%Rsp)+,%I0x%x\n" :
- "\tfmovem %Rsp@+,%I0x%x\n",
- current_frame.fpu_rev_mask);
+ m68k_emit_movem (stack_pointer_rtx, 0,
+ current_frame.fpu_no, FP0_REG,
+ current_frame.fpu_mask, false, true);
}
else
- {
- if (MOTOROLA && !TARGET_COLDFIRE)
- asm_fprintf (stream, "\tfmovm -%wd(%s),%I0x%x\n",
- current_frame.foffset + fsize,
- M68K_REGNAME (FRAME_POINTER_REGNUM),
- current_frame.fpu_rev_mask);
- else
- asm_fprintf (stream, "\tfmovem %s@(-%wd),%I0x%x\n",
- M68K_REGNAME (FRAME_POINTER_REGNUM),
- current_frame.foffset + fsize,
- current_frame.fpu_rev_mask);
- }
+ m68k_emit_movem (frame_pointer_rtx,
+ -(current_frame.foffset + fsize),
+ current_frame.fpu_no, FP0_REG,
+ current_frame.fpu_mask, false, false);
}
+
if (frame_pointer_needed)
- fprintf (stream, "\tunlk %s\n", M68K_REGNAME (FRAME_POINTER_REGNUM));
+ emit_insn (gen_unlink (frame_pointer_rtx));
else if (fsize_with_regs)
- {
- if (fsize_with_regs <= 8)
- {
- if (!TARGET_COLDFIRE)
- asm_fprintf (stream, "\taddq" ASM_DOT "w %I%wd,%Rsp\n",
- fsize_with_regs);
- else
- asm_fprintf (stream, "\taddq" ASM_DOT "l %I%wd,%Rsp\n",
- fsize_with_regs);
- }
- else if (fsize_with_regs <= 16 && TUNE_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 (stream,
- "\taddq" ASM_DOT "w %I8,%Rsp\n"
- "\taddq" ASM_DOT "w %I%wd,%Rsp\n",
- fsize_with_regs - 8);
- }
- else if (fsize_with_regs < 0x8000)
- {
- if (TUNE_68040)
- asm_fprintf (stream, "\tadd" ASM_DOT "w %I%wd,%Rsp\n",
- fsize_with_regs);
- else
- asm_fprintf (stream, (MOTOROLA
- ? "\tlea (%wd,%Rsp),%Rsp\n"
- : "\tlea %Rsp@(%wd),%Rsp\n"),
- fsize_with_regs);
- }
- else
- asm_fprintf (stream, "\tadd" ASM_DOT "l %I%wd,%Rsp\n", fsize_with_regs);
- }
+ emit_insn (gen_addsi3 (stack_pointer_rtx,
+ stack_pointer_rtx,
+ GEN_INT (fsize_with_regs)));
+
if (current_function_calls_eh_return)
- asm_fprintf (stream, "\tadd" ASM_DOT "l %Ra0,%Rsp\n");
- if (m68k_interrupt_function_p (current_function_decl))
- fprintf (stream, "\trte\n");
- else if (current_function_pops_args)
- asm_fprintf (stream, "\trtd %I%d\n", current_function_pops_args);
- else
- fprintf (stream, "\trts\n");
+ emit_insn (gen_addsi3 (stack_pointer_rtx,
+ stack_pointer_rtx,
+ EH_RETURN_STACKADJ_RTX));
+
+ emit_insn (gen_rtx_RETURN (VOIDmode));
}
/* Return true if X is a valid comparison operator for the dbcc
@@ -3110,6 +2942,204 @@ split_di (rtx operands[], int num, rtx lo_half[], rtx hi_half[])
}
}
+/* Split X into a base and a constant offset, storing them in *BASE
+ and *OFFSET respectively. */
+
+static void
+m68k_split_offset (rtx x, rtx *base, HOST_WIDE_INT *offset)
+{
+ *offset = 0;
+ if (GET_CODE (x) == PLUS && GET_CODE (XEXP (x, 1)) == CONST_INT)
+ {
+ *offset += INTVAL (XEXP (x, 1));
+ x = XEXP (x, 0);
+ }
+ *base = x;
+}
+
+/* Return true if PATTERN is a PARALLEL suitable for a movem or fmovem
+ instruction. STORE_P says whether the move is a load or store.
+
+ If the instruction uses post-increment or pre-decrement addressing,
+ AUTOMOD_BASE is the base register and AUTOMOD_OFFSET is the total
+ adjustment. This adjustment will be made by the first element of
+ PARALLEL, with the loads or stores starting at element 1. If the
+ instruction does not use post-increment or pre-decrement addressing,
+ AUTOMOD_BASE is null, AUTOMOD_OFFSET is 0, and the loads or stores
+ start at element 0. */
+
+bool
+m68k_movem_pattern_p (rtx pattern, rtx automod_base,
+ HOST_WIDE_INT automod_offset, bool store_p)
+{
+ rtx base, mem_base, set, mem, reg, last_reg;
+ HOST_WIDE_INT offset, mem_offset;
+ int i, first, len;
+ enum reg_class rclass;
+
+ len = XVECLEN (pattern, 0);
+ first = (automod_base != NULL);
+
+ if (automod_base)
+ {
+ /* Stores must be pre-decrement and loads must be post-increment. */
+ if (store_p != (automod_offset < 0))
+ return false;
+
+ /* Work out the base and offset for lowest memory location. */
+ base = automod_base;
+ offset = (automod_offset < 0 ? automod_offset : 0);
+ }
+ else
+ {
+ /* Allow any valid base and offset in the first access. */
+ base = NULL;
+ offset = 0;
+ }
+
+ last_reg = NULL;
+ rclass = NO_REGS;
+ for (i = first; i < len; i++)
+ {
+ /* We need a plain SET. */
+ set = XVECEXP (pattern, 0, i);
+ if (GET_CODE (set) != SET)
+ return false;
+
+ /* Check that we have a memory location... */
+ mem = XEXP (set, !store_p);
+ if (!MEM_P (mem) || !memory_operand (mem, VOIDmode))
+ return false;
+
+ /* ...with the right address. */
+ if (base == NULL)
+ {
+ m68k_split_offset (XEXP (mem, 0), &base, &offset);
+ /* The ColdFire instruction only allows (An) and (d16,An) modes.
+ There are no mode restrictions for 680x0 besides the
+ automodification rules enforced above. */
+ if (TARGET_COLDFIRE
+ && !m68k_legitimate_base_reg_p (base, reload_completed))
+ return false;
+ }
+ else
+ {
+ m68k_split_offset (XEXP (mem, 0), &mem_base, &mem_offset);
+ if (!rtx_equal_p (base, mem_base) || offset != mem_offset)
+ return false;
+ }
+
+ /* Check that we have a register of the required mode and class. */
+ reg = XEXP (set, store_p);
+ if (!REG_P (reg)
+ || !HARD_REGISTER_P (reg)
+ || GET_MODE (reg) != reg_raw_mode[REGNO (reg)])
+ return false;
+
+ if (last_reg)
+ {
+ /* The register must belong to RCLASS and have a higher number
+ than the register in the previous SET. */
+ if (!TEST_HARD_REG_BIT (reg_class_contents[rclass], REGNO (reg))
+ || REGNO (last_reg) >= REGNO (reg))
+ return false;
+ }
+ else
+ {
+ /* Work out which register class we need. */
+ if (INT_REGNO_P (REGNO (reg)))
+ rclass = GENERAL_REGS;
+ else if (FP_REGNO_P (REGNO (reg)))
+ rclass = FP_REGS;
+ else
+ return false;
+ }
+
+ last_reg = reg;
+ offset += GET_MODE_SIZE (GET_MODE (reg));
+ }
+
+ /* If we have an automodification, check whether the final offset is OK. */
+ if (automod_base && offset != (automod_offset < 0 ? 0 : automod_offset))
+ return false;
+
+ /* Reject unprofitable cases. */
+ if (len < first + (rclass == FP_REGS ? MIN_FMOVEM_REGS : MIN_MOVEM_REGS))
+ return false;
+
+ return true;
+}
+
+/* Return the assembly code template for a movem or fmovem instruction
+ whose pattern is given by PATTERN. Store the template's operands
+ in OPERANDS.
+
+ If the instruction uses post-increment or pre-decrement addressing,
+ AUTOMOD_OFFSET is the total adjustment, otherwise it is 0. STORE_P
+ is true if this is a store instruction. */
+
+const char *
+m68k_output_movem (rtx *operands, rtx pattern,
+ HOST_WIDE_INT automod_offset, bool store_p)
+{
+ unsigned int mask;
+ int i, first;
+
+ gcc_assert (GET_CODE (pattern) == PARALLEL);
+ mask = 0;
+ first = (automod_offset != 0);
+ for (i = first; i < XVECLEN (pattern, 0); i++)
+ {
+ /* When using movem with pre-decrement addressing, register X + D0_REG
+ is controlled by bit 15 - X. For all other addressing modes,
+ register X + D0_REG is controlled by bit X. Confusingly, the
+ register mask for fmovem is in the opposite order to that for
+ movem. */
+ unsigned int regno;
+
+ gcc_assert (MEM_P (XEXP (XVECEXP (pattern, 0, i), !store_p)));
+ gcc_assert (REG_P (XEXP (XVECEXP (pattern, 0, i), store_p)));
+ regno = REGNO (XEXP (XVECEXP (pattern, 0, i), store_p));
+ if (automod_offset < 0)
+ {
+ if (FP_REGNO_P (regno))
+ mask |= 1 << (regno - FP0_REG);
+ else
+ mask |= 1 << (15 - (regno - D0_REG));
+ }
+ else
+ {
+ if (FP_REGNO_P (regno))
+ mask |= 1 << (7 - (regno - FP0_REG));
+ else
+ mask |= 1 << (regno - D0_REG);
+ }
+ }
+ CC_STATUS_INIT;
+
+ if (automod_offset == 0)
+ operands[0] = XEXP (XEXP (XVECEXP (pattern, 0, first), !store_p), 0);
+ else if (automod_offset < 0)
+ operands[0] = gen_rtx_PRE_DEC (Pmode, SET_DEST (XVECEXP (pattern, 0, 0)));
+ else
+ operands[0] = gen_rtx_POST_INC (Pmode, SET_DEST (XVECEXP (pattern, 0, 0)));
+ operands[1] = GEN_INT (mask);
+ if (FP_REGNO_P (REGNO (XEXP (XVECEXP (pattern, 0, first), store_p))))
+ {
+ if (store_p)
+ return MOTOROLA ? "fmovm %1,%a0" : "fmovem %1,%a0";
+ else
+ return MOTOROLA ? "fmovm %a0,%1" : "fmovem %a0,%1";
+ }
+ else
+ {
+ if (store_p)
+ return MOTOROLA ? "movm.l %1,%a0" : "moveml %1,%a0";
+ else
+ return MOTOROLA ? "movm.l %a0,%1" : "moveml %a0,%1";
+ }
+}
+
/* Return a REG that occurs in ADDR with coefficient 1.
ADDR can be effectively incremented by incrementing REG. */
@@ -3481,6 +3511,7 @@ floating_exact_log2 (rtx x)
'$' 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.
+ '?' for m68k_library_id_string
'b' for byte insn (no effect, on the Sun; this is for the ISI).
'd' to force memory addressing to be absolute, not relative.
@@ -3520,6 +3551,8 @@ print_operand (FILE *file, rtx op, int letter)
}
else if (letter == '/')
asm_fprintf (file, "%R");
+ else if (letter == '?')
+ asm_fprintf (file, m68k_library_id_string);
else if (letter == 'p')
{
output_addr_const (file, op);
diff --git a/gcc/config/m68k/m68k.h b/gcc/config/m68k/m68k.h
index 0465688..c4b1e14 100644
--- a/gcc/config/m68k/m68k.h
+++ b/gcc/config/m68k/m68k.h
@@ -987,6 +987,10 @@ do { if (cc_prev_status.flags & CC_IN_68881) \
/* Before the prologue, the top of the frame is at 4(%sp). */
#define INCOMING_FRAME_SP_OFFSET 4
+/* All registers are live on exit from an interrupt routine. */
+#define EPILOGUE_USES(REGNO) \
+ (reload_completed && m68k_interrupt_function_p (current_function_decl))
+
/* Describe how we implement __builtin_eh_return. */
#define EH_RETURN_DATA_REGNO(N) \
((N) < 2 ? (N) : INVALID_REGNUM)
@@ -1123,6 +1127,7 @@ do { if (cc_prev_status.flags & CC_IN_68881) \
'$' 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.
+ '?' for m68k_library_id_string
'b' for byte insn (no effect, on the Sun; this is for the ISI).
'd' to force memory addressing to be absolute, not relative.
@@ -1133,7 +1138,7 @@ do { if (cc_prev_status.flags & CC_IN_68881) \
#define PRINT_OPERAND_PUNCT_VALID_P(CODE) \
((CODE) == '.' || (CODE) == '#' || (CODE) == '-' \
|| (CODE) == '+' || (CODE) == '@' || (CODE) == '!' \
- || (CODE) == '$' || (CODE) == '&' || (CODE) == '/')
+ || (CODE) == '$' || (CODE) == '&' || (CODE) == '/' || (CODE) == '?')
/* See m68k.c for the m68k specific codes. */
diff --git a/gcc/config/m68k/m68k.md b/gcc/config/m68k/m68k.md
index a2f0cfd..4c0878a 100644
--- a/gcc/config/m68k/m68k.md
+++ b/gcc/config/m68k/m68k.md
@@ -112,8 +112,9 @@
;; UNSPEC usage:
(define_constants
- [(UNSPEC_SIN 1)
- (UNSPEC_COS 2)
+ [(UNSPEC_SIN 1)
+ (UNSPEC_COS 2)
+ (UNSPEC_GOT 3)
])
;; UNSPEC_VOLATILE usage:
@@ -126,7 +127,10 @@
(define_constants
[(D0_REG 0)
(A0_REG 8)
+ (A1_REG 9)
+ (PIC_REG 13)
(SP_REG 15)
+ (FP0_REG 16)
])
(include "predicates.md")
@@ -6756,15 +6760,158 @@
""
"nop")
+(define_expand "prologue"
+ [(const_int 0)]
+ ""
+{
+ m68k_expand_prologue ();
+ DONE;
+})
+
+(define_expand "epilogue"
+ [(return)]
+ ""
+{
+ m68k_expand_epilogue ();
+ DONE;
+})
+
;; Used for frameless functions which save no regs and allocate no locals.
-(define_insn "return"
+(define_expand "return"
[(return)]
"m68k_use_return_insn ()"
+ "")
+
+(define_insn "*return"
+ [(return)]
+ ""
{
- if (current_function_pops_args == 0)
+ if (m68k_interrupt_function_p (current_function_decl))
+ return "rte";
+ else if (current_function_pops_args)
+ {
+ operands[0] = GEN_INT (current_function_pops_args);
+ return "rtd %0";
+ }
+ else
return "rts";
- operands[0] = GEN_INT (current_function_pops_args);
- return "rtd %0";
+})
+
+(define_insn "*m68k_store_multiple"
+ [(match_parallel 0 "" [(match_operand 1 "")])]
+ "m68k_movem_pattern_p (operands[0], NULL, 0, true)"
+{
+ return m68k_output_movem (operands, operands[0], 0, true);
+})
+
+(define_insn "*m68k_store_multiple_automod"
+ [(match_parallel 0 ""
+ [(set (match_operand:SI 1 "register_operand" "=a")
+ (plus:SI (match_operand:SI 2 "register_operand" "1")
+ (match_operand:SI 3 "const_int_operand")))])]
+ "m68k_movem_pattern_p (operands[0], operands[1], INTVAL (operands[3]), true)"
+{
+ return m68k_output_movem (operands, operands[0], INTVAL (operands[3]), true);
+})
+
+(define_insn "*m68k_load_multiple"
+ [(match_parallel 0 "" [(match_operand 1 "")])]
+ "m68k_movem_pattern_p (operands[0], NULL, 0, false)"
+{
+ return m68k_output_movem (operands, operands[0], 0, false);
+})
+
+(define_insn "*m68k_load_multiple_automod"
+ [(match_parallel 0 ""
+ [(set (match_operand:SI 1 "register_operand" "=a")
+ (plus:SI (match_operand:SI 2 "register_operand" "1")
+ (match_operand:SI 3 "const_int_operand")))])]
+ "m68k_movem_pattern_p (operands[0], operands[1],
+ INTVAL (operands[3]), false)"
+{
+ return m68k_output_movem (operands, operands[0],
+ INTVAL (operands[3]), false);
+})
+
+(define_expand "link"
+ [(parallel
+ [(set (match_operand:SI 0 "register_operand")
+ (plus:SI (reg:SI SP_REG) (const_int -4)))
+ (set (match_dup 2)
+ (match_dup 0))
+ (set (reg:SI SP_REG)
+ (plus:SI (reg:SI SP_REG)
+ (match_operand:SI 1 "const_int_operand")))])]
+ "TARGET_68020 || INTVAL (operands[1]) >= -0x8004"
+{
+ operands[2] = gen_frame_mem (SImode, plus_constant (stack_pointer_rtx, -4));
+})
+
+(define_insn "*link"
+ [(set (match_operand:SI 0 "register_operand" "+r")
+ (plus:SI (reg:SI SP_REG) (const_int -4)))
+ (set (mem:SI (plus:SI (reg:SI SP_REG) (const_int -4)))
+ (match_dup 0))
+ (set (reg:SI SP_REG)
+ (plus:SI (reg:SI SP_REG)
+ (match_operand:SI 1 "const_int_operand")))]
+ "TARGET_68020 || INTVAL (operands[1]) >= -0x8004"
+{
+ operands[1] = GEN_INT (INTVAL (operands[1]) + 4);
+ if (!MOTOROLA)
+ return "link %0,%1";
+ else if (INTVAL (operands[1]) >= -0x8000)
+ return "link.w %0,%1";
+ else
+ return "link.l %0,%1";
+})
+
+(define_expand "unlink"
+ [(parallel
+ [(set (match_operand:SI 0 "register_operand")
+ (match_dup 1))
+ (set (reg:SI SP_REG)
+ (plus:SI (match_dup 0)
+ (const_int 4)))])]
+ ""
+{
+ operands[1] = gen_frame_mem (SImode, copy_rtx (operands[0]));
+})
+
+(define_insn "*unlink"
+ [(set (match_operand:SI 0 "register_operand" "+r")
+ (mem:SI (match_dup 0)))
+ (set (reg:SI SP_REG)
+ (plus:SI (match_dup 0)
+ (const_int 4)))]
+ ""
+ "unlk %0")
+
+(define_insn "load_got"
+ [(set (match_operand:SI 0 "register_operand" "=a")
+ (unspec:SI [(const_int 0)] UNSPEC_GOT))]
+ ""
+{
+ if (TARGET_ID_SHARED_LIBRARY)
+ {
+ operands[1] = gen_rtx_REG (Pmode, PIC_REG);
+ return MOTOROLA ? "move.l %?(%1),%0" : "movel %1@(%?), %0";
+ }
+ else if (MOTOROLA)
+ {
+ if (TARGET_COLDFIRE)
+ /* Load the full 32-bit PC-relative offset of
+ _GLOBAL_OFFSET_TABLE_ into the PIC register, then use it to
+ calculate the absolute value. The offset and "lea"
+ operation word together occupy 6 bytes. */
+ return ("move.l #_GLOBAL_OFFSET_TABLE_@GOTPC, %0\n\t"
+ "lea (-6, %%pc, %0), %0");
+ else
+ return "lea (%%pc, _GLOBAL_OFFSET_TABLE_@GOTPC), %0";
+ }
+ else
+ return ("movel #_GLOBAL_OFFSET_TABLE_, %0\n\t"
+ "lea %%pc@(0,%0:l),%0");
})
(define_insn "indirect_jump"