Commit a40ed0f3 by Kazu Hirata Committed by Richard Sandiford

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
parent fc2241eb
2007-03-06 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.
2007-03-06 Richard Sandiford <richard@codesourcery.com>
PR target/23482
......
......@@ -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 *);
......
......@@ -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);
......
......@@ -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. */
......
......@@ -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"
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment