diff options
author | Kazu Hirata <kazu@codesourcery.com> | 2007-03-06 08:58:40 +0000 |
---|---|---|
committer | Richard Sandiford <rsandifo@gcc.gnu.org> | 2007-03-06 08:58:40 +0000 |
commit | a40ed0f3103b04e249f2ad78ae36e328f2d6f1cd (patch) | |
tree | 4ec6be1791e9c668d0c60ecc8d9f725a3d0d2609 /gcc/config | |
parent | fc2241eb94b9b29bd2613ea02224a78fdb242a95 (diff) | |
download | gcc-a40ed0f3103b04e249f2ad78ae36e328f2d6f1cd.tar.gz |
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.h | 5 | ||||
-rw-r--r-- | gcc/config/m68k/m68k.c | 929 | ||||
-rw-r--r-- | gcc/config/m68k/m68k.h | 7 | ||||
-rw-r--r-- | gcc/config/m68k/m68k.md | 159 |
4 files changed, 645 insertions, 455 deletions
diff --git a/gcc/config/m68k/m68k-protos.h b/gcc/config/m68k/m68k-protos.h index ba137d6848c..a6705c0f1a0 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 57e15c82de4..1d6c03546d5 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 04656885a49..c4b1e14842c 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 a2f0cfd7044..4c0878a1df2 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" |