X-Git-Url: https://oss.titaniummirror.com/gitweb/?a=blobdiff_plain;ds=sidebyside;f=gcc%2Fconfig%2Fm68k%2Fm68k.c;fp=gcc%2Fconfig%2Fm68k%2Fm68k.c;h=2f931c6be428bbfaddcf75d98a24886ec9eda301;hb=6fed43773c9b0ce596dca5686f37ac3fc0fa11c0;hp=42c4f7cc287823bf7ce7648cfa9facbbcb8e5400;hpb=27b11d56b743098deb193d510b337ba22dc52e5c;p=msp430-gcc.git diff --git a/gcc/config/m68k/m68k.c b/gcc/config/m68k/m68k.c index 42c4f7cc..2f931c6b 100644 --- a/gcc/config/m68k/m68k.c +++ b/gcc/config/m68k/m68k.c @@ -1,26 +1,28 @@ /* Subroutines for insn-output.c for Motorola 68000 family. - Copyright (C) 1987, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001 + Copyright (C) 1987, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, + 2001, 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc. -This file is part of GNU CC. +This file is part of GCC. -GNU CC is free software; you can redistribute it and/or modify +GCC is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2, or (at your option) +the Free Software Foundation; either version 3, or (at your option) any later version. -GNU CC is distributed in the hope that it will be useful, +GCC is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License -along with GNU CC; see the file COPYING. If not, write to -the Free Software Foundation, 59 Temple Place - Suite 330, -Boston, MA 02111-1307, USA. */ +along with GCC; see the file COPYING3. If not see +. */ #include "config.h" #include "system.h" +#include "coretypes.h" +#include "tm.h" #include "tree.h" #include "rtl.h" #include "function.h" @@ -39,48 +41,119 @@ Boston, MA 02111-1307, USA. */ #include "target.h" #include "target-def.h" #include "debug.h" - -/* Needed for use_return_insn. */ #include "flags.h" +#include "df.h" +/* ??? Need to add a dependency between m68k.o and sched-int.h. */ +#include "sched-int.h" +#include "insn-codes.h" + +enum reg_class regno_reg_class[] = +{ + DATA_REGS, DATA_REGS, DATA_REGS, DATA_REGS, + DATA_REGS, DATA_REGS, DATA_REGS, DATA_REGS, + ADDR_REGS, ADDR_REGS, ADDR_REGS, ADDR_REGS, + ADDR_REGS, ADDR_REGS, ADDR_REGS, ADDR_REGS, + FP_REGS, FP_REGS, FP_REGS, FP_REGS, + FP_REGS, FP_REGS, FP_REGS, FP_REGS, + ADDR_REGS +}; + + +/* 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 +{ + /* Stack pointer to frame pointer offset. */ + HOST_WIDE_INT offset; + + /* Offset of FPU registers. */ + HOST_WIDE_INT foffset; + + /* Frame size in bytes (rounded up). */ + HOST_WIDE_INT size; + + /* Data and address register. */ + int reg_no; + unsigned int reg_mask; + + /* FPU registers. */ + int fpu_no; + unsigned int fpu_mask; + + /* Offsets relative to ARG_POINTER. */ + HOST_WIDE_INT frame_pointer_offset; + HOST_WIDE_INT stack_pointer_offset; + + /* Function which the above information refers to. */ + int funcdef_no; +}; -#ifdef SUPPORT_SUN_FPA +/* Current frame information calculated by m68k_compute_frame_layout(). */ +static struct m68k_frame current_frame; -/* Index into this array by (register number >> 3) to find the - smallest class which contains that register. */ -const enum reg_class regno_reg_class[] - = { DATA_REGS, ADDR_REGS, FP_REGS, - LO_FPA_REGS, LO_FPA_REGS, FPA_REGS, FPA_REGS }; +/* Structure describing an m68k address. -#endif /* defined SUPPORT_SUN_FPA */ + If CODE is UNKNOWN, the address is BASE + INDEX * SCALE + OFFSET, + with null fields evaluating to 0. Here: -/* This flag is used to communicate between movhi and ASM_OUTPUT_CASE_END, - if SGS_SWITCH_TABLE. */ -int switch_table_difference_label_flag; + - BASE satisfies m68k_legitimate_base_reg_p + - INDEX satisfies m68k_legitimate_index_reg_p + - OFFSET satisfies m68k_legitimate_constant_address_p -static rtx find_addr_reg PARAMS ((rtx)); -static const char *singlemove_string PARAMS ((rtx *)); -static void m68k_output_function_prologue PARAMS ((FILE *, HOST_WIDE_INT)); -static void m68k_output_function_epilogue PARAMS ((FILE *, HOST_WIDE_INT)); -static void m68k_coff_asm_named_section PARAMS ((const char *, unsigned int)); -#ifdef CTOR_LIST_BEGIN -static void m68k_svr3_asm_out_constructor PARAMS ((rtx, int)); + INDEX is either HImode or SImode. The other fields are SImode. + + If CODE is PRE_DEC, the address is -(BASE). If CODE is POST_INC, + the address is (BASE)+. */ +struct m68k_address { + enum rtx_code code; + rtx base; + rtx index; + rtx offset; + int scale; +}; + +static int m68k_sched_adjust_cost (rtx, rtx, rtx, int); +static int m68k_sched_issue_rate (void); +static int m68k_sched_variable_issue (FILE *, int, rtx, int); +static void m68k_sched_md_init_global (FILE *, int, int); +static void m68k_sched_md_finish_global (FILE *, int); +static void m68k_sched_md_init (FILE *, int, int); +static void m68k_sched_dfa_pre_advance_cycle (void); +static void m68k_sched_dfa_post_advance_cycle (void); +static int m68k_sched_first_cycle_multipass_dfa_lookahead (void); + +static bool m68k_handle_option (size_t, const char *, int); +static rtx find_addr_reg (rtx); +static const char *singlemove_string (rtx *); +#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 tree m68k_handle_fndecl_attribute (tree *node, tree name, + tree args, int flags, + bool *no_add_attrs); +static void m68k_compute_frame_layout (void); +static bool m68k_save_reg (unsigned int regno, bool interrupt_handler); +static bool m68k_ok_for_sibcall_p (tree, tree); +static bool m68k_rtx_costs (rtx, int, int, int *, bool); +#if M68K_HONOR_TARGET_STRICT_ALIGNMENT +static bool m68k_return_in_memory (const_tree, const_tree); #endif -/* Alignment to use for loops and jumps */ -/* Specify power of two alignment used for loops. */ -const char *m68k_align_loops_string; -/* Specify power of two alignment used for non-loop jumps. */ -const char *m68k_align_jumps_string; -/* Specify power of two alignment used for functions. */ -const char *m68k_align_funcs_string; - -/* Specify power of two alignment used for loops. */ -int m68k_align_loops; -/* Specify power of two alignment used for non-loop jumps. */ -int m68k_align_jumps; -/* Specify power of two alignment used for functions. */ -int m68k_align_funcs; +/* Specify the identification number of the library being built */ +const char *m68k_library_id_string = "_current_shared_library_a5_offset_"; /* Nonzero if the last compare/test insn had FP operands. The sCC expanders peek at this to determine what to do for the @@ -117,13 +190,328 @@ 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 +#define TARGET_ASM_CAN_OUTPUT_MI_THUNK hook_bool_const_tree_hwi_hwi_const_tree_true + +#undef TARGET_ASM_FILE_START_APP_OFF +#define TARGET_ASM_FILE_START_APP_OFF true + +#undef TARGET_SCHED_ADJUST_COST +#define TARGET_SCHED_ADJUST_COST m68k_sched_adjust_cost + +#undef TARGET_SCHED_ISSUE_RATE +#define TARGET_SCHED_ISSUE_RATE m68k_sched_issue_rate + +#undef TARGET_SCHED_VARIABLE_ISSUE +#define TARGET_SCHED_VARIABLE_ISSUE m68k_sched_variable_issue + +#undef TARGET_SCHED_INIT_GLOBAL +#define TARGET_SCHED_INIT_GLOBAL m68k_sched_md_init_global + +#undef TARGET_SCHED_FINISH_GLOBAL +#define TARGET_SCHED_FINISH_GLOBAL m68k_sched_md_finish_global + +#undef TARGET_SCHED_INIT +#define TARGET_SCHED_INIT m68k_sched_md_init + +#undef TARGET_SCHED_DFA_PRE_ADVANCE_CYCLE +#define TARGET_SCHED_DFA_PRE_ADVANCE_CYCLE m68k_sched_dfa_pre_advance_cycle + +#undef TARGET_SCHED_DFA_POST_ADVANCE_CYCLE +#define TARGET_SCHED_DFA_POST_ADVANCE_CYCLE m68k_sched_dfa_post_advance_cycle + +#undef TARGET_SCHED_FIRST_CYCLE_MULTIPASS_DFA_LOOKAHEAD +#define TARGET_SCHED_FIRST_CYCLE_MULTIPASS_DFA_LOOKAHEAD \ + m68k_sched_first_cycle_multipass_dfa_lookahead + +#undef TARGET_HANDLE_OPTION +#define TARGET_HANDLE_OPTION m68k_handle_option + +#undef TARGET_RTX_COSTS +#define TARGET_RTX_COSTS m68k_rtx_costs + +#undef TARGET_ATTRIBUTE_TABLE +#define TARGET_ATTRIBUTE_TABLE m68k_attribute_table + +#undef TARGET_PROMOTE_PROTOTYPES +#define TARGET_PROMOTE_PROTOTYPES hook_bool_const_tree_true + +#undef TARGET_STRUCT_VALUE_RTX +#define TARGET_STRUCT_VALUE_RTX m68k_struct_value_rtx + +#undef TARGET_CANNOT_FORCE_CONST_MEM +#define TARGET_CANNOT_FORCE_CONST_MEM m68k_illegitimate_symbolic_constant_p + +#undef TARGET_FUNCTION_OK_FOR_SIBCALL +#define TARGET_FUNCTION_OK_FOR_SIBCALL m68k_ok_for_sibcall_p + +#if M68K_HONOR_TARGET_STRICT_ALIGNMENT +#undef TARGET_RETURN_IN_MEMORY +#define TARGET_RETURN_IN_MEMORY m68k_return_in_memory +#endif + +static const struct attribute_spec m68k_attribute_table[] = +{ + /* { name, min_len, max_len, decl_req, type_req, fn_type_req, handler } */ + { "interrupt", 0, 0, true, false, false, m68k_handle_fndecl_attribute }, + { "interrupt_handler", 0, 0, true, false, false, m68k_handle_fndecl_attribute }, + { "interrupt_thread", 0, 0, true, false, false, m68k_handle_fndecl_attribute }, + { NULL, 0, 0, false, false, false, NULL } +}; struct gcc_target targetm = TARGET_INITIALIZER; +/* Base flags for 68k ISAs. */ +#define FL_FOR_isa_00 FL_ISA_68000 +#define FL_FOR_isa_10 (FL_FOR_isa_00 | FL_ISA_68010) +/* FL_68881 controls the default setting of -m68881. gcc has traditionally + generated 68881 code for 68020 and 68030 targets unless explicitly told + not to. */ +#define FL_FOR_isa_20 (FL_FOR_isa_10 | FL_ISA_68020 \ + | FL_BITFIELD | FL_68881) +#define FL_FOR_isa_40 (FL_FOR_isa_20 | FL_ISA_68040) +#define FL_FOR_isa_cpu32 (FL_FOR_isa_10 | FL_ISA_68020) + +/* Base flags for ColdFire ISAs. */ +#define FL_FOR_isa_a (FL_COLDFIRE | FL_ISA_A) +#define FL_FOR_isa_aplus (FL_FOR_isa_a | FL_ISA_APLUS | FL_CF_USP) +/* Note ISA_B doesn't necessarily include USP (user stack pointer) support. */ +#define FL_FOR_isa_b (FL_FOR_isa_a | FL_ISA_B | FL_CF_HWDIV) +/* ISA_C is not upwardly compatible with ISA_B. */ +#define FL_FOR_isa_c (FL_FOR_isa_a | FL_ISA_C | FL_CF_USP) + +enum m68k_isa +{ + /* Traditional 68000 instruction sets. */ + isa_00, + isa_10, + isa_20, + isa_40, + isa_cpu32, + /* ColdFire instruction set variants. */ + isa_a, + isa_aplus, + isa_b, + isa_c, + isa_max +}; + +/* Information about one of the -march, -mcpu or -mtune arguments. */ +struct m68k_target_selection +{ + /* The argument being described. */ + const char *name; + + /* For -mcpu, this is the device selected by the option. + For -mtune and -march, it is a representative device + for the microarchitecture or ISA respectively. */ + enum target_device device; + + /* The M68K_DEVICE fields associated with DEVICE. See the comment + in m68k-devices.def for details. FAMILY is only valid for -mcpu. */ + const char *family; + enum uarch_type microarch; + enum m68k_isa isa; + unsigned long flags; +}; + +/* A list of all devices in m68k-devices.def. Used for -mcpu selection. */ +static const struct m68k_target_selection all_devices[] = +{ +#define M68K_DEVICE(NAME,ENUM_VALUE,FAMILY,MULTILIB,MICROARCH,ISA,FLAGS) \ + { NAME, ENUM_VALUE, FAMILY, u##MICROARCH, ISA, FLAGS | FL_FOR_##ISA }, +#include "m68k-devices.def" +#undef M68K_DEVICE + { NULL, unk_device, NULL, unk_arch, isa_max, 0 } +}; + +/* A list of all ISAs, mapping each one to a representative device. + Used for -march selection. */ +static const struct m68k_target_selection all_isas[] = +{ + { "68000", m68000, NULL, u68000, isa_00, FL_FOR_isa_00 }, + { "68010", m68010, NULL, u68010, isa_10, FL_FOR_isa_10 }, + { "68020", m68020, NULL, u68020, isa_20, FL_FOR_isa_20 }, + { "68030", m68030, NULL, u68030, isa_20, FL_FOR_isa_20 }, + { "68040", m68040, NULL, u68040, isa_40, FL_FOR_isa_40 }, + { "68060", m68060, NULL, u68060, isa_40, FL_FOR_isa_40 }, + { "cpu32", cpu32, NULL, ucpu32, isa_20, FL_FOR_isa_cpu32 }, + { "isaa", mcf5206e, NULL, ucfv2, isa_a, (FL_FOR_isa_a + | FL_CF_HWDIV) }, + { "isaaplus", mcf5271, NULL, ucfv2, isa_aplus, (FL_FOR_isa_aplus + | FL_CF_HWDIV) }, + { "isab", mcf5407, NULL, ucfv4, isa_b, FL_FOR_isa_b }, + { "isac", unk_device, NULL, ucfv4, isa_c, (FL_FOR_isa_c + | FL_CF_HWDIV) }, + { NULL, unk_device, NULL, unk_arch, isa_max, 0 } +}; + +/* A list of all microarchitectures, mapping each one to a representative + device. Used for -mtune selection. */ +static const struct m68k_target_selection all_microarchs[] = +{ + { "68000", m68000, NULL, u68000, isa_00, FL_FOR_isa_00 }, + { "68010", m68010, NULL, u68010, isa_10, FL_FOR_isa_10 }, + { "68020", m68020, NULL, u68020, isa_20, FL_FOR_isa_20 }, + { "68020-40", m68020, NULL, u68020_40, isa_20, FL_FOR_isa_20 }, + { "68020-60", m68020, NULL, u68020_60, isa_20, FL_FOR_isa_20 }, + { "68030", m68030, NULL, u68030, isa_20, FL_FOR_isa_20 }, + { "68040", m68040, NULL, u68040, isa_40, FL_FOR_isa_40 }, + { "68060", m68060, NULL, u68060, isa_40, FL_FOR_isa_40 }, + { "cpu32", cpu32, NULL, ucpu32, isa_20, FL_FOR_isa_cpu32 }, + { "cfv1", mcf51qe, NULL, ucfv1, isa_c, FL_FOR_isa_c }, + { "cfv2", mcf5206, NULL, ucfv2, isa_a, FL_FOR_isa_a }, + { "cfv3", mcf5307, NULL, ucfv3, isa_a, (FL_FOR_isa_a + | FL_CF_HWDIV) }, + { "cfv4", mcf5407, NULL, ucfv4, isa_b, FL_FOR_isa_b }, + { "cfv4e", mcf547x, NULL, ucfv4e, isa_b, (FL_FOR_isa_b + | FL_CF_USP + | FL_CF_EMAC + | FL_CF_FPU) }, + { NULL, unk_device, NULL, unk_arch, isa_max, 0 } +}; + +/* The entries associated with the -mcpu, -march and -mtune settings, + or null for options that have not been used. */ +const struct m68k_target_selection *m68k_cpu_entry; +const struct m68k_target_selection *m68k_arch_entry; +const struct m68k_target_selection *m68k_tune_entry; + +/* Which CPU we are generating code for. */ +enum target_device m68k_cpu; + +/* Which microarchitecture to tune for. */ +enum uarch_type m68k_tune; + +/* Which FPU to use. */ +enum fpu_type m68k_fpu; + +/* The set of FL_* flags that apply to the target processor. */ +unsigned int m68k_cpu_flags; + +/* The set of FL_* flags that apply to the processor to be tuned for. */ +unsigned int m68k_tune_flags; + +/* Asm templates for calling or jumping to an arbitrary symbolic address, + or NULL if such calls or jumps are not supported. The address is held + in operand 0. */ +const char *m68k_symbolic_call; +const char *m68k_symbolic_jump; + +/* Enum variable that corresponds to m68k_symbolic_call values. */ +enum M68K_SYMBOLIC_CALL m68k_symbolic_call_var; + + +/* See whether TABLE has an entry with name NAME. Return true and + store the entry in *ENTRY if so, otherwise return false and + leave *ENTRY alone. */ + +static bool +m68k_find_selection (const struct m68k_target_selection **entry, + const struct m68k_target_selection *table, + const char *name) +{ + size_t i; + + for (i = 0; table[i].name; i++) + if (strcmp (table[i].name, name) == 0) + { + *entry = table + i; + return true; + } + return false; +} + +/* Implement TARGET_HANDLE_OPTION. */ + +static bool +m68k_handle_option (size_t code, const char *arg, int value) +{ + switch (code) + { + case OPT_march_: + return m68k_find_selection (&m68k_arch_entry, all_isas, arg); + + case OPT_mcpu_: + return m68k_find_selection (&m68k_cpu_entry, all_devices, arg); + + case OPT_mtune_: + return m68k_find_selection (&m68k_tune_entry, all_microarchs, arg); + + case OPT_m5200: + return m68k_find_selection (&m68k_cpu_entry, all_devices, "5206"); + + case OPT_m5206e: + return m68k_find_selection (&m68k_cpu_entry, all_devices, "5206e"); + + case OPT_m528x: + return m68k_find_selection (&m68k_cpu_entry, all_devices, "528x"); + + case OPT_m5307: + return m68k_find_selection (&m68k_cpu_entry, all_devices, "5307"); + + case OPT_m5407: + return m68k_find_selection (&m68k_cpu_entry, all_devices, "5407"); + + case OPT_mcfv4e: + return m68k_find_selection (&m68k_cpu_entry, all_devices, "547x"); + + case OPT_m68000: + case OPT_mc68000: + return m68k_find_selection (&m68k_cpu_entry, all_devices, "68000"); + + case OPT_m68010: + return m68k_find_selection (&m68k_cpu_entry, all_devices, "68010"); + + case OPT_m68020: + case OPT_mc68020: + return m68k_find_selection (&m68k_cpu_entry, all_devices, "68020"); + + case OPT_m68020_40: + return (m68k_find_selection (&m68k_tune_entry, all_microarchs, + "68020-40") + && m68k_find_selection (&m68k_cpu_entry, all_devices, "68020")); + + case OPT_m68020_60: + return (m68k_find_selection (&m68k_tune_entry, all_microarchs, + "68020-60") + && m68k_find_selection (&m68k_cpu_entry, all_devices, "68020")); + + case OPT_m68030: + return m68k_find_selection (&m68k_cpu_entry, all_devices, "68030"); + + case OPT_m68040: + return m68k_find_selection (&m68k_cpu_entry, all_devices, "68040"); + + case OPT_m68060: + return m68k_find_selection (&m68k_cpu_entry, all_devices, "68060"); + + case OPT_m68302: + return m68k_find_selection (&m68k_cpu_entry, all_devices, "68302"); + + case OPT_m68332: + case OPT_mcpu32: + return m68k_find_selection (&m68k_cpu_entry, all_devices, "68332"); + + case OPT_mshared_library_id_: + if (value > MAX_LIBRARY_ID) + error ("-mshared-library-id=%s is not between 0 and %d", + arg, MAX_LIBRARY_ID); + else + { + char *tmp; + asprintf (&tmp, "%d", (value * -4) - 4); + m68k_library_id_string = tmp; + } + return true; + + default: + return true; + } +} + /* Sometimes certain combinations of command options do not make sense on a particular target machine. You can define a macro `OVERRIDE_OPTIONS' to take account of this. This macro, if @@ -134,1546 +522,982 @@ struct gcc_target targetm = TARGET_INITIALIZER; `-O'. That is what `OPTIMIZATION_OPTIONS' is for. */ void -override_options () +override_options (void) { - int def_align; - int i; + const struct m68k_target_selection *entry; + unsigned long target_mask; + + /* User can choose: + + -mcpu= + -march= + -mtune= + + -march=ARCH should generate code that runs any processor + implementing architecture ARCH. -mcpu=CPU should override -march + and should generate code that runs on processor CPU, making free + use of any instructions that CPU understands. -mtune=UARCH applies + on top of -mcpu or -march and optimizes the code for UARCH. It does + not change the target architecture. */ + if (m68k_cpu_entry) + { + /* Complain if the -march setting is for a different microarchitecture, + or includes flags that the -mcpu setting doesn't. */ + if (m68k_arch_entry + && (m68k_arch_entry->microarch != m68k_cpu_entry->microarch + || (m68k_arch_entry->flags & ~m68k_cpu_entry->flags) != 0)) + warning (0, "-mcpu=%s conflicts with -march=%s", + m68k_cpu_entry->name, m68k_arch_entry->name); + + entry = m68k_cpu_entry; + } + else + entry = m68k_arch_entry; + + if (!entry) + entry = all_devices + TARGET_CPU_DEFAULT; + + m68k_cpu_flags = entry->flags; - def_align = 1; + /* Use the architecture setting to derive default values for + certain flags. */ + target_mask = 0; - /* Validate -malign-loops= value, or provide default */ - m68k_align_loops = def_align; - if (m68k_align_loops_string) + /* ColdFire is lenient about alignment. */ + if (!TARGET_COLDFIRE) + target_mask |= MASK_STRICT_ALIGNMENT; + + if ((m68k_cpu_flags & FL_BITFIELD) != 0) + target_mask |= MASK_BITFIELD; + if ((m68k_cpu_flags & FL_CF_HWDIV) != 0) + target_mask |= MASK_CF_HWDIV; + if ((m68k_cpu_flags & (FL_68881 | FL_CF_FPU)) != 0) + target_mask |= MASK_HARD_FLOAT; + target_flags |= target_mask & ~target_flags_explicit; + + /* Set the directly-usable versions of the -mcpu and -mtune settings. */ + m68k_cpu = entry->device; + if (m68k_tune_entry) { - i = atoi (m68k_align_loops_string); - if (i < 1 || i > MAX_CODE_ALIGN) - error ("-malign-loops=%d is not between 1 and %d", i, MAX_CODE_ALIGN); - else - m68k_align_loops = i; + m68k_tune = m68k_tune_entry->microarch; + m68k_tune_flags = m68k_tune_entry->flags; } - - /* Validate -malign-jumps= value, or provide default */ - m68k_align_jumps = def_align; - if (m68k_align_jumps_string) +#ifdef M68K_DEFAULT_TUNE + else if (!m68k_cpu_entry && !m68k_arch_entry) { - i = atoi (m68k_align_jumps_string); - if (i < 1 || i > MAX_CODE_ALIGN) - error ("-malign-jumps=%d is not between 1 and %d", i, MAX_CODE_ALIGN); - else - m68k_align_jumps = i; + enum target_device dev; + dev = all_microarchs[M68K_DEFAULT_TUNE].device; + m68k_tune_flags = all_devices[dev]->flags; + } +#endif + else + { + m68k_tune = entry->microarch; + m68k_tune_flags = entry->flags; } - /* Validate -malign-functions= value, or provide default */ - m68k_align_funcs = def_align; - if (m68k_align_funcs_string) + /* Set the type of FPU. */ + m68k_fpu = (!TARGET_HARD_FLOAT ? FPUTYPE_NONE + : (m68k_cpu_flags & FL_COLDFIRE) != 0 ? FPUTYPE_COLDFIRE + : FPUTYPE_68881); + + /* Sanity check to ensure that msep-data and mid-sahred-library are not + * both specified together. Doing so simply doesn't make sense. + */ + if (TARGET_SEP_DATA && TARGET_ID_SHARED_LIBRARY) + error ("cannot specify both -msep-data and -mid-shared-library"); + + /* If we're generating code for a separate A5 relative data segment, + * we've got to enable -fPIC as well. This might be relaxable to + * -fpic but it hasn't been tested properly. + */ + if (TARGET_SEP_DATA || TARGET_ID_SHARED_LIBRARY) + flag_pic = 2; + + /* -mpcrel -fPIC uses 32-bit pc-relative displacements. Raise an + error if the target does not support them. */ + if (TARGET_PCREL && !TARGET_68020 && flag_pic == 2) + error ("-mpcrel -fPIC is not currently supported on selected cpu"); + + /* ??? A historic way of turning on pic, or is this intended to + be an embedded thing that doesn't have the same name binding + significance that it does on hosted ELF systems? */ + if (TARGET_PCREL && flag_pic == 0) + flag_pic = 1; + + if (!flag_pic) { - i = atoi (m68k_align_funcs_string); - if (i < 1 || i > MAX_CODE_ALIGN) - error ("-malign-functions=%d is not between 1 and %d", - i, MAX_CODE_ALIGN); + m68k_symbolic_call_var = M68K_SYMBOLIC_CALL_JSR; + + m68k_symbolic_jump = "jra %a0"; + } + else if (TARGET_ID_SHARED_LIBRARY) + /* All addresses must be loaded from the GOT. */ + ; + else if (TARGET_68020 || TARGET_ISAB || TARGET_ISAC) + { + if (TARGET_PCREL) + m68k_symbolic_call_var = M68K_SYMBOLIC_CALL_BSR_C; + else + m68k_symbolic_call_var = M68K_SYMBOLIC_CALL_BSR_P; + + if (TARGET_ISAC) + /* No unconditional long branch */; + else if (TARGET_PCREL) + m68k_symbolic_jump = "bra%.l %c0"; else - m68k_align_funcs = i; + m68k_symbolic_jump = "bra%.l %p0"; + /* Turn off function cse if we are doing PIC. We always want + function call to be done as `bsr foo@PLTPC'. */ + /* ??? It's traditional to do this for -mpcrel too, but it isn't + clear how intentional that is. */ + flag_no_function_cse = 1; } -} - -/* This function generates the assembly code for function entry. - STREAM is a stdio stream to output the code to. - SIZE is an int: how many units of temporary storage to allocate. - Refer to the array `regs_ever_live' to determine which registers - to save; `regs_ever_live[I]' is nonzero if register number I - is ever used in the function. This function is responsible for - knowing which registers should not be saved even if used. */ + switch (m68k_symbolic_call_var) + { + case M68K_SYMBOLIC_CALL_JSR: + m68k_symbolic_call = "jsr %a0"; + break; -/* Note that the order of the bit mask for fmovem is the opposite - of the order for movem! */ + case M68K_SYMBOLIC_CALL_BSR_C: + m68k_symbolic_call = "bsr%.l %c0"; + break; -#ifdef CRDS + case M68K_SYMBOLIC_CALL_BSR_P: + m68k_symbolic_call = "bsr%.l %p0"; + break; -static void -m68k_output_function_prologue (stream, size) - FILE *stream; - HOST_WIDE_INT size; -{ - register int regno; - register int mask = 0; - HOST_WIDE_INT fsize = ((size) + 3) & -4; + case M68K_SYMBOLIC_CALL_NONE: + gcc_assert (m68k_symbolic_call == NULL); + break; - /* unos stack probe */ - if (fsize > 30000) + default: + gcc_unreachable (); + } + +#ifndef ASM_OUTPUT_ALIGN_WITH_NOP + if (align_labels > 2) { - fprintf (stream, "\tmovel sp,a0\n"); - fprintf (stream, "\taddl $-%d,a0\n", 2048 + fsize); - fprintf (stream, "\ttstb (a0)\n"); + warning (0, "-falign-labels=%d is not supported", align_labels); + align_labels = 0; } - else - fprintf (stream, "\ttstb -%d(sp)\n", 2048 + fsize); + if (align_loops > 2) + { + warning (0, "-falign-loops=%d is not supported", align_loops); + align_loops = 0; + } +#endif - if (frame_pointer_needed) + SUBTARGET_OVERRIDE_OPTIONS; + + /* Setup scheduling options. */ + if (TUNE_CFV1) + m68k_sched_cpu = CPU_CFV1; + else if (TUNE_CFV2) + m68k_sched_cpu = CPU_CFV2; + else if (TUNE_CFV3) + m68k_sched_cpu = CPU_CFV3; + else if (TUNE_CFV4) + m68k_sched_cpu = CPU_CFV4; + else { - if (TARGET_68020 || fsize < 0x8000) - fprintf (stream, "\tlink a6,$%d\n", -fsize); - else - fprintf (stream, "\tlink a6,$0\n\tsubl $%d,sp\n", fsize); + m68k_sched_cpu = CPU_UNKNOWN; + flag_schedule_insns = 0; + flag_schedule_insns_after_reload = 0; + flag_modulo_sched = 0; } - else if (fsize) + + if (m68k_sched_cpu != CPU_UNKNOWN) { - /* Adding negative number is faster on the 68040. */ - if (fsize + 4 < 0x8000) - fprintf (stream, "\tadd.w #%d,sp\n", - (fsize + 4)); + if ((m68k_cpu_flags & (FL_CF_EMAC | FL_CF_EMAC_B)) != 0) + m68k_sched_mac = MAC_CF_EMAC; + else if ((m68k_cpu_flags & FL_CF_MAC) != 0) + m68k_sched_mac = MAC_CF_MAC; else - fprintf (stream, "\tadd.l #%d,sp\n", - (fsize + 4)); + m68k_sched_mac = MAC_NO; } +} - for (regno = 16; regno < 24; regno++) - if (regs_ever_live[regno] && ! call_used_regs[regno]) - mask |= 1 << (regno - 16); +/* Generate a macro of the form __mPREFIX_cpu_NAME, where PREFIX is the + given argument and NAME is the argument passed to -mcpu. Return NULL + if -mcpu was not passed. */ - if ((mask & 0xff) != 0) - fprintf (stream, "\tfmovem $0x%x,-(sp)\n", mask & 0xff); +const char * +m68k_cpp_cpu_ident (const char *prefix) +{ + if (!m68k_cpu_entry) + return NULL; + return concat ("__m", prefix, "_cpu_", m68k_cpu_entry->name, NULL); +} - mask = 0; - for (regno = 0; regno < 16; regno++) - if (regs_ever_live[regno] && ! call_used_regs[regno]) - mask |= 1 << (15 - regno); - if (frame_pointer_needed) - mask &= ~ (1 << (15-FRAME_POINTER_REGNUM)); +/* Generate a macro of the form __mPREFIX_family_NAME, where PREFIX is the + given argument and NAME is the name of the representative device for + the -mcpu argument's family. Return NULL if -mcpu was not passed. */ - if (exact_log2 (mask) >= 0) - fprintf (stream, "\tmovel %s,-(sp)\n", reg_names[15 - exact_log2 (mask)]); - else if (mask) - fprintf (stream, "\tmovem $0x%x,-(sp)\n", mask); +const char * +m68k_cpp_cpu_family (const char *prefix) +{ + if (!m68k_cpu_entry) + return NULL; + return concat ("__m", prefix, "_family_", m68k_cpu_entry->family, NULL); } + +/* Return m68k_fk_interrupt_handler if FUNC has an "interrupt" or + "interrupt_handler" attribute and interrupt_thread if FUNC has an + "interrupt_thread" attribute. Otherwise, return + m68k_fk_normal_function. */ -#else -#if defined (DPX2) && defined (MOTOROLA) +enum m68k_function_kind +m68k_get_function_kind (tree func) +{ + tree a; -static void -m68k_output_function_prologue (stream, size) - FILE *stream; - HOST_WIDE_INT size; + gcc_assert (TREE_CODE (func) == FUNCTION_DECL); + + a = lookup_attribute ("interrupt", DECL_ATTRIBUTES (func)); + if (a != NULL_TREE) + return m68k_fk_interrupt_handler; + + a = lookup_attribute ("interrupt_handler", DECL_ATTRIBUTES (func)); + if (a != NULL_TREE) + return m68k_fk_interrupt_handler; + + a = lookup_attribute ("interrupt_thread", DECL_ATTRIBUTES (func)); + if (a != NULL_TREE) + return m68k_fk_interrupt_thread; + + return m68k_fk_normal_function; +} + +/* Handle an attribute requiring a FUNCTION_DECL; arguments as in + struct attribute_spec.handler. */ +static tree +m68k_handle_fndecl_attribute (tree *node, tree name, + tree args ATTRIBUTE_UNUSED, + int flags ATTRIBUTE_UNUSED, + bool *no_add_attrs) { - register int regno; - register int mask = 0; - int num_saved_regs = 0, first = 1; - HOST_WIDE_INT fsize = ((size) + 3) & -4; + if (TREE_CODE (*node) != FUNCTION_DECL) + { + warning (OPT_Wattributes, "%qs attribute only applies to functions", + IDENTIFIER_POINTER (name)); + *no_add_attrs = true; + } - if (frame_pointer_needed) + if (m68k_get_function_kind (*node) != m68k_fk_normal_function) { - /* Adding negative number is faster on the 68040. */ - if (fsize < 0x8000 && !TARGET_68040) - fprintf (stream, "\tlink %s,#%d\n", - reg_names[FRAME_POINTER_REGNUM], -fsize); - else if (TARGET_68020) - fprintf (stream, "\tlink %s,#%d\n", - reg_names[FRAME_POINTER_REGNUM], -fsize); - else - fprintf (stream, "\tlink %s,#0\n\tadd.l #%d,sp\n", - reg_names[FRAME_POINTER_REGNUM], -fsize); + error ("multiple interrupt attributes not allowed"); + *no_add_attrs = true; } - else if (fsize) + + if (!TARGET_FIDOA + && !strcmp (IDENTIFIER_POINTER (name), "interrupt_thread")) { - /* Adding negative number is faster on the 68040. */ - if (fsize + 4 < 0x8000) - fprintf (stream, "\tadd.w #%d,sp\n", - (fsize + 4)); - else - fprintf (stream, "\tadd.l #%d,sp\n", - (fsize + 4)); + error ("interrupt_thread is available only on fido"); + *no_add_attrs = true; } - for (regno = 23; regno >= 16; regno--) - if (regs_ever_live[regno] && ! call_used_regs[regno]) - { - if (first) - { - fprintf (stream, "\tfmovem.x %s", reg_names[regno]); - first = 0; - } - else - fprintf (stream, "/%s", reg_names[regno]); - } - if (!first) - fprintf (stream, ",-(sp)\n"); + return NULL_TREE; +} - mask = 0; - for (regno = 0; regno < 16; regno++) - if (regs_ever_live[regno] && ! call_used_regs[regno]) - { - mask |= 1 << (15 - regno); - num_saved_regs++; - } +static void +m68k_compute_frame_layout (void) +{ + int regno, saved; + unsigned int mask; + enum m68k_function_kind func_kind = + m68k_get_function_kind (current_function_decl); + bool interrupt_handler = func_kind == m68k_fk_interrupt_handler; + bool interrupt_thread = func_kind == m68k_fk_interrupt_thread; + + /* Only compute the frame once per function. + Don't cache information until reload has been completed. */ + if (current_frame.funcdef_no == current_function_funcdef_no + && reload_completed) + return; + + current_frame.size = (get_frame_size () + 3) & -4; + + mask = saved = 0; + + /* Interrupt thread does not need to save any register. */ + if (!interrupt_thread) + for (regno = 0; regno < 16; regno++) + if (m68k_save_reg (regno, interrupt_handler)) + { + mask |= 1 << (regno - D0_REG); + saved++; + } + current_frame.offset = saved * 4; + current_frame.reg_no = saved; + current_frame.reg_mask = mask; - if (frame_pointer_needed) + current_frame.foffset = 0; + mask = saved = 0; + if (TARGET_HARD_FLOAT) { - mask &= ~ (1 << (15 - FRAME_POINTER_REGNUM)); - num_saved_regs--; + /* Interrupt thread does not need to save any register. */ + if (!interrupt_thread) + for (regno = 16; regno < 24; regno++) + if (m68k_save_reg (regno, interrupt_handler)) + { + mask |= 1 << (regno - FP0_REG); + saved++; + } + current_frame.foffset = saved * TARGET_FP_REG_SIZE; + current_frame.offset += current_frame.foffset; } + current_frame.fpu_no = saved; + current_frame.fpu_mask = mask; + + /* Remember what function this frame refers to. */ + current_frame.funcdef_no = current_function_funcdef_no; +} - if (num_saved_regs <= 2) +HOST_WIDE_INT +m68k_initial_elimination_offset (int from, int to) +{ + int argptr_offset; + /* The arg pointer points 8 bytes before the start of the arguments, + as defined by FIRST_PARM_OFFSET. This makes it coincident with the + frame pointer in most frames. */ + argptr_offset = frame_pointer_needed ? 0 : UNITS_PER_WORD; + if (from == ARG_POINTER_REGNUM && to == FRAME_POINTER_REGNUM) + return argptr_offset; + + m68k_compute_frame_layout (); + + gcc_assert (to == STACK_POINTER_REGNUM); + switch (from) { - /* 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 */ + case ARG_POINTER_REGNUM: + return current_frame.offset + current_frame.size - argptr_offset; + case FRAME_POINTER_REGNUM: + return current_frame.offset + current_frame.size; + default: + gcc_unreachable (); + } +} - int i; +/* Refer to the array `regs_ever_live' to determine which registers + to save; `regs_ever_live[I]' is nonzero if register number I + is ever used in the function. This function is responsible for + knowing which registers should not be saved even if used. + Return true if we need to save REGNO. */ - /* Undo the work from above. */ - for (i = 0; i< 16; i++) - if (mask & (1 << i)) - fprintf (stream, "\tmove.l %s,-(sp)\n", reg_names[15 - i]); +static bool +m68k_save_reg (unsigned int regno, bool interrupt_handler) +{ + if (flag_pic && regno == PIC_REG) + { + if (crtl->saves_all_registers) + return true; + if (crtl->uses_pic_offset_table) + return true; + /* Reload may introduce constant pool references into a function + that thitherto didn't need a PIC register. Note that the test + above will not catch that case because we will only set + crtl->uses_pic_offset_table when emitting + the address reloads. */ + if (crtl->uses_const_pool) + return true; } - else if (mask) + + if (crtl->calls_eh_return) { - first = 1; - for (regno = 0; regno < 16; regno++) - if (mask & (1 << regno)) - { - if (first) - { - fprintf (stream, "\tmovem.l %s", reg_names[15 - regno]); - first = 0; - } - else - fprintf (stream, "/%s", reg_names[15 - regno]); - } - fprintf (stream, ",-(sp)\n"); + unsigned int i; + for (i = 0; ; i++) + { + unsigned int test = EH_RETURN_DATA_REGNO (i); + if (test == INVALID_REGNUM) + break; + if (test == regno) + return true; + } } - if (flag_pic && current_function_uses_pic_offset_table) + /* Fixed regs we never touch. */ + if (fixed_regs[regno]) + return false; + + /* The frame pointer (if it is such) is handled specially. */ + if (regno == FRAME_POINTER_REGNUM && frame_pointer_needed) + return false; + + /* Interrupt handlers must also save call_used_regs + if they are live or when calling nested functions. */ + if (interrupt_handler) { - fprintf (stream, "\tmove.l #__GLOBAL_OFFSET_TABLE_, %s\n", - reg_names[PIC_OFFSET_TABLE_REGNUM]); - fprintf (stream, "\tlea.l (pc,%s.l),%s\n", - reg_names[PIC_OFFSET_TABLE_REGNUM], - reg_names[PIC_OFFSET_TABLE_REGNUM]); + if (df_regs_ever_live_p (regno)) + return true; + + if (!current_function_is_leaf && call_used_regs[regno]) + return true; } + + /* Never need to save registers that aren't touched. */ + if (!df_regs_ever_live_p (regno)) + return false; + + /* Otherwise save everything that isn't call-clobbered. */ + return !call_used_regs[regno]; } -#else -#if defined (NEWS) && defined (MOTOROLA) +/* 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 void -m68k_output_function_prologue (stream, size) - FILE *stream; - HOST_WIDE_INT size; +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) { - register int regno; - register int mask = 0; - HOST_WIDE_INT fsize = ((size) + 3) & -4; + int i; + rtx body, addr, src, operands[2]; + enum machine_mode mode; - if (frame_pointer_needed) - { - if (fsize < 0x8000) - fprintf (stream, "\tlink fp,#%d\n", -fsize); - else if (TARGET_68020) - fprintf (stream, "\tlink.l fp,#%d\n", -fsize); - else - fprintf (stream, "\tlink fp,#0\n\tsub.l #%d,sp\n", fsize); - } - else if (fsize) + body = gen_rtx_PARALLEL (VOIDmode, rtvec_alloc (adjust_stack_p + count)); + mode = reg_raw_mode[regno]; + i = 0; + + if (adjust_stack_p) { - int amt = fsize + 4; - /* Adding negative number is faster on the 68040. */ - if (fsize + 4 < 0x8000) - asm_fprintf (stream, "\tadd.w %0I%d,%Rsp\n", - amt); - else - asm_fprintf (stream, "\tadd.l %0I%d,%Rsp\n", - amt); + 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 (regno = 16; regno < FIRST_PSEUDO_REGISTER; regno++) - if (regs_ever_live[regno] && ! call_used_regs[regno]) - mask |= 1 << (regno - 16); + 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)); - if (mask != 0) - fprintf (stream, "\tfmovem.x #0x%x,-(sp)\n", mask & 0xff); + return emit_insn (body); +} - mask = 0; - for (regno = 0; regno < 16; regno++) - if (regs_ever_live[regno] && ! call_used_regs[regno]) - mask |= 1 << (15 - regno); +/* Make INSN a frame-related instruction. */ - if (frame_pointer_needed) - mask &= ~ (1 << (15-FRAME_POINTER_REGNUM)); +static void +m68k_set_frame_related (rtx insn) +{ + rtx body; + int i; - if (exact_log2 (mask) >= 0) - fprintf (stream, "\tmove.l %s,-(sp)\n", reg_names[15 - exact_log2 (mask)]); - else - if (mask) fprintf (stream, "\tmovem.l #0x%x,-(sp)\n", mask); + 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; } -#else /* !CRDS && ! (NEWS && MOTOROLA) && ! (DPX2 && MOTOROLA) */ +/* Emit RTL for the "prologue" define_expand. */ -static void -m68k_output_function_prologue (stream, size) - FILE *stream; - HOST_WIDE_INT size; +void +m68k_expand_prologue (void) { - register int regno; - register int mask = 0; - int num_saved_regs = 0; - HOST_WIDE_INT fsize = (size + 3) & -4; - HOST_WIDE_INT cfa_offset = INCOMING_FRAME_SP_OFFSET; - HOST_WIDE_INT cfa_store_offset = cfa_offset; - + HOST_WIDE_INT fsize_with_regs; + rtx limit, src, dest, insn; + + 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 + if (crtl->limit_stack && GET_CODE (stack_limit_rtx) == SYMBOL_REF) { -#if defined (MOTOROLA) - asm_fprintf (stream, "\tcmp.l %0I%s+%d,%Rsp\n\ttrapcs\n", - XSTR (stack_limit_rtx, 0), fsize + 4); -#else - asm_fprintf (stream, "\tcmpl %0I%s+%d,%Rsp\n\ttrapcs\n", - XSTR (stack_limit_rtx, 0), fsize + 4); -#endif + 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)); + } + + fsize_with_regs = current_frame.size; + if (TARGET_COLDFIRE) + { + /* 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 (fsize == 0 && TARGET_68040) - { - /* on the 68040, pea + move is faster than link.w 0 */ -#ifdef MOTOROLA - asm_fprintf (stream, "\tpea (%s)\n\tmove.l %s,%s\n", - reg_names[FRAME_POINTER_REGNUM], reg_names[STACK_POINTER_REGNUM], - reg_names[FRAME_POINTER_REGNUM]); -#else - asm_fprintf (stream, "\tpea %s@\n\tmovel %s,%s\n", - reg_names[FRAME_POINTER_REGNUM], reg_names[STACK_POINTER_REGNUM], - reg_names[FRAME_POINTER_REGNUM]); -#endif - } - else if (fsize < 0x8000) + if (fsize_with_regs == 0 && TUNE_68040) { -#ifdef MOTOROLA - asm_fprintf (stream, "\tlink.w %s,%0I%d\n", - reg_names[FRAME_POINTER_REGNUM], -fsize); -#else - asm_fprintf (stream, "\tlink %s,%0I%d\n", - reg_names[FRAME_POINTER_REGNUM], -fsize); -#endif - } - else if (TARGET_68020) - { -#ifdef MOTOROLA - asm_fprintf (stream, "\tlink.l %s,%0I%d\n", - reg_names[FRAME_POINTER_REGNUM], -fsize); -#else - asm_fprintf (stream, "\tlink %s,%0I%d\n", - reg_names[FRAME_POINTER_REGNUM], -fsize); -#endif + /* 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 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 - { - /* Adding negative number is faster on the 68040. */ -#ifdef MOTOROLA - asm_fprintf (stream, "\tlink.w %s,%0I0\n\tadd.l %0I%d,%Rsp\n", - reg_names[FRAME_POINTER_REGNUM], -fsize); -#else - asm_fprintf (stream, "\tlink %s,%0I0\n\taddl %0I%d,%Rsp\n", - reg_names[FRAME_POINTER_REGNUM], -fsize); -#endif - } - if (dwarf2out_do_frame ()) - { - char *l; - l = (char *) dwarf2out_cfi_label (); - cfa_store_offset += 4; - cfa_offset = cfa_store_offset; - dwarf2out_reg_save (l, FRAME_POINTER_REGNUM, -cfa_store_offset); - dwarf2out_def_cfa (l, FRAME_POINTER_REGNUM, cfa_offset); - cfa_store_offset += fsize; + { + 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)))); } + + /* If the frame pointer is needed, emit a special barrier that + will prevent the scheduler from moving stores to the frame + before the stack adjustment. */ + emit_insn (gen_stack_tie (stack_pointer_rtx, frame_pointer_rtx)); } - else if (fsize) + 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) { - if (fsize + 4 < 0x8000) + gcc_assert (current_frame.fpu_no >= MIN_FMOVEM_REGS); + if (TARGET_68881) + 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 { -#ifndef NO_ADDSUB_Q - if (fsize + 4 <= 8) - { - if (!TARGET_5200) - { - /* asm_fprintf() cannot handle %. */ -#ifdef MOTOROLA - asm_fprintf (stream, "\tsubq.w %0I%d,%Rsp\n", fsize + 4); -#else - asm_fprintf (stream, "\tsubqw %0I%d,%Rsp\n", fsize + 4); -#endif - } - else - { - /* asm_fprintf() cannot handle %. */ -#ifdef MOTOROLA - asm_fprintf (stream, "\tsubq.l %0I%d,%Rsp\n", fsize + 4); -#else - asm_fprintf (stream, "\tsubql %0I%d,%Rsp\n", fsize + 4); -#endif - } - } - else if (fsize + 4 <= 16 && TARGET_CPU32) - { - /* On the CPU32 it is faster to use two subqw instructions to - subtract a small integer (8 < N <= 16) to a register. */ - /* asm_fprintf() cannot handle %. */ -#ifdef MOTOROLA - asm_fprintf (stream, "\tsubq.w %0I8,%Rsp\n\tsubq.w %0I%d,%Rsp\n", - fsize + 4 - 8); -#else - asm_fprintf (stream, "\tsubqw %0I8,%Rsp\n\tsubqw %0I%d,%Rsp\n", - fsize + 4 - 8); -#endif - } - else -#endif /* not NO_ADDSUB_Q */ - if (TARGET_68040) - { - /* Adding negative number is faster on the 68040. */ - /* asm_fprintf() cannot handle %. */ -#ifdef MOTOROLA - asm_fprintf (stream, "\tadd.w %0I%d,%Rsp\n", - (fsize + 4)); -#else - asm_fprintf (stream, "\taddw %0I%d,%Rsp\n", - (fsize + 4)); -#endif - } - else - { -#ifdef MOTOROLA - asm_fprintf (stream, "\tlea (%d,%Rsp),%Rsp\n", - (fsize + 4)); -#else - asm_fprintf (stream, "\tlea %Rsp@(%d),%Rsp\n", - (fsize + 4)); -#endif - } - } - else - { - /* asm_fprintf() cannot handle %. */ -#ifdef MOTOROLA - asm_fprintf (stream, "\tadd.l %0I%d,%Rsp\n", - (fsize + 4)); -#else - asm_fprintf (stream, "\taddl %0I%d,%Rsp\n", - (fsize + 4)); -#endif - } - if (dwarf2out_do_frame ()) - { - cfa_store_offset += fsize; - cfa_offset = cfa_store_offset; - dwarf2out_def_cfa ("", STACK_POINTER_REGNUM, cfa_offset); - } - } -#ifdef SUPPORT_SUN_FPA - for (regno = 24; regno < 56; regno++) - if (regs_ever_live[regno] && ! call_used_regs[regno]) - { -#ifdef MOTOROLA - asm_fprintf (stream, "\tfpmovd %s,-(%Rsp)\n", - reg_names[regno]); -#else - asm_fprintf (stream, "\tfpmoved %s,%Rsp@-\n", - reg_names[regno]); -#endif - if (dwarf2out_do_frame ()) - { - char *l = dwarf2out_cfi_label (); - - cfa_store_offset += 8; - if (! frame_pointer_needed) - { - cfa_offset = cfa_store_offset; - dwarf2out_def_cfa (l, STACK_POINTER_REGNUM, cfa_offset); - } - dwarf2out_reg_save (l, regno, -cfa_store_offset); - } - } -#endif - if (TARGET_68881) - { - for (regno = 16; regno < 24; regno++) - if (regs_ever_live[regno] && ! call_used_regs[regno]) - { - mask |= 1 << (regno - 16); - num_saved_regs++; - } - if ((mask & 0xff) != 0) - { -#ifdef MOTOROLA - asm_fprintf (stream, "\tfmovm %0I0x%x,-(%Rsp)\n", mask & 0xff); -#else - asm_fprintf (stream, "\tfmovem %0I0x%x,%Rsp@-\n", mask & 0xff); -#endif - if (dwarf2out_do_frame ()) - { - char *l = (char *) dwarf2out_cfi_label (); - int n_regs; + int offset; - cfa_store_offset += num_saved_regs * 12; - if (! frame_pointer_needed) - { - cfa_offset = cfa_store_offset; - dwarf2out_def_cfa (l, STACK_POINTER_REGNUM, cfa_offset); - } - for (regno = 16, n_regs = 0; regno < 24; regno++) - if (mask & (1 << (regno - 16))) - dwarf2out_reg_save (l, regno, - -cfa_store_offset + n_regs++ * 12); - } + /* 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 * 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)); } - mask = 0; - num_saved_regs = 0; - } - for (regno = 0; regno < 16; regno++) - if (regs_ever_live[regno] && ! call_used_regs[regno]) - { - mask |= 1 << (15 - regno); - num_saved_regs++; - } - if (frame_pointer_needed) - { - mask &= ~ (1 << (15 - FRAME_POINTER_REGNUM)); - num_saved_regs--; - } - if (flag_pic && current_function_uses_pic_offset_table) - { - mask |= 1 << (15 - PIC_OFFSET_TABLE_REGNUM); - num_saved_regs++; } -#if NEED_PROBE -#ifdef MOTOROLA - asm_fprintf (stream, "\ttst.l %d(%Rsp)\n", NEED_PROBE - num_saved_regs * 4); -#else - asm_fprintf (stream, "\ttstl %Rsp@(%d)\n", NEED_PROBE - num_saved_regs * 4); -#endif -#endif - - /* If the stack limit is not a symbol, check it here. + /* If the stack limit is not a symbol, check it here. This has the disadvantage that it may be too late... */ - if (current_function_limit_stack) + if (crtl->limit_stack) { if (REG_P (stack_limit_rtx)) { -#if defined (MOTOROLA) - asm_fprintf (stream, "\tcmp.l %s,%Rsp\n\ttrapcs\n", - reg_names[REGNO (stack_limit_rtx)]); -#else - asm_fprintf (stream, "\tcmpl %s,%Rsp\n\ttrapcs\n", - reg_names[REGNO (stack_limit_rtx)]); -#endif + 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 ("stack limit expression is not supported"); + warning (0, "stack limit expression is not supported"); } - - if (num_saved_regs <= 2) - { - /* Store each separately in the same order moveml uses. - Using two movel instructions instead of a single moveml - is about 15% faster for the 68020 and 68030 at no expense - in code size */ + if (current_frame.reg_no < MIN_MOVEM_REGS) + { + /* Store each register separately in the same order moveml does. */ int i; - /* Undo the work from above. */ - for (i = 0; i< 16; i++) - if (mask & (1 << i)) + for (i = 16; i-- > 0; ) + if (current_frame.reg_mask & (1 << i)) { - asm_fprintf (stream, -#ifdef MOTOROLA - "\t%Omove.l %s,-(%Rsp)\n", -#else - "\tmovel %s,%Rsp@-\n", -#endif - reg_names[15 - i]); - if (dwarf2out_do_frame ()) - { - char *l = (char *) dwarf2out_cfi_label (); - - cfa_store_offset += 4; - if (! frame_pointer_needed) - { - cfa_offset = cfa_store_offset; - dwarf2out_def_cfa (l, STACK_POINTER_REGNUM, cfa_offset); - } - dwarf2out_reg_save (l, 15 - i, -cfa_store_offset); - } + 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 (mask) + else { - if (TARGET_5200) - { - /* The coldfire does not support the predecrement form of the - movml instruction, so we must adjust the stack pointer and - then use the plain address register indirect mode. We also - have to invert the register save mask to use the new mode. - - FIXME: if num_saved_regs was calculated earlier, we could - combine the stack pointer adjustment with any adjustment - done when the initial stack frame is created. This would - save an instruction */ - - int newmask = 0; - int i; - - for (i = 0; i < 16; i++) - if (mask & (1 << i)) - newmask |= (1 << (15-i)); - -#ifdef MOTOROLA - asm_fprintf (stream, "\tlea (%d,%Rsp),%Rsp\n", -num_saved_regs*4); - asm_fprintf (stream, "\tmovm.l %0I0x%x,(%Rsp)\n", newmask); -#else - asm_fprintf (stream, "\tlea %Rsp@(%d),%Rsp\n", -num_saved_regs*4); - asm_fprintf (stream, "\tmoveml %0I0x%x,%Rsp@\n", newmask); -#endif - } + if (TARGET_COLDFIRE) + /* 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 - { -#ifdef MOTOROLA - asm_fprintf (stream, "\tmovm.l %0I0x%x,-(%Rsp)\n", mask); -#else - asm_fprintf (stream, "\tmoveml %0I0x%x,%Rsp@-\n", mask); -#endif - } - if (dwarf2out_do_frame ()) - { - char *l = (char *) dwarf2out_cfi_label (); - int n_regs; - - cfa_store_offset += num_saved_regs * 4; - if (! frame_pointer_needed) - { - cfa_offset = cfa_store_offset; - dwarf2out_def_cfa (l, STACK_POINTER_REGNUM, cfa_offset); - } - for (regno = 0, n_regs = 0; regno < 16; regno++) - if (mask & (1 << (15 - regno))) - dwarf2out_reg_save (l, regno, - -cfa_store_offset + n_regs++ * 4); - } - } - if (flag_pic && current_function_uses_pic_offset_table) - { -#ifdef MOTOROLA - asm_fprintf (stream, "\t%Olea (%Rpc, %U_GLOBAL_OFFSET_TABLE_@GOTPC), %s\n", - reg_names[PIC_OFFSET_TABLE_REGNUM]); -#else - asm_fprintf (stream, "\tmovel %0I__GLOBAL_OFFSET_TABLE_, %s\n", - reg_names[PIC_OFFSET_TABLE_REGNUM]); - asm_fprintf (stream, "\tlea %Rpc@(0,%s:l),%s\n", - reg_names[PIC_OFFSET_TABLE_REGNUM], - reg_names[PIC_OFFSET_TABLE_REGNUM]); -#endif + 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 (flag_pic + && !TARGET_SEP_DATA + && crtl->uses_pic_offset_table) + insn = emit_insn (gen_load_got (pic_offset_table_rtx)); } -#endif /* ! (DPX2 && MOTOROLA) */ -#endif /* ! (NEWS && MOTOROLA) */ -#endif /* !CRDS */ -/* Return true if this function's epilogue can be output as RTL. */ +/* Return true if a simple (return) instruction is sufficient for this + instruction (i.e. if no epilogue is needed). */ -int -use_return_insn () +bool +m68k_use_return_insn (void) { - int regno; - if (!reload_completed || frame_pointer_needed || get_frame_size () != 0) - return 0; - - /* Copied from output_function_epilogue (). We should probably create a - separate layout routine to perform the common work. */ - - for (regno = 0 ; regno < FIRST_PSEUDO_REGISTER ; regno++) - if (regs_ever_live[regno] && ! call_used_regs[regno]) - return 0; + return false; - if (flag_pic && current_function_uses_pic_offset_table) - return 0; - - return 1; + m68k_compute_frame_layout (); + return current_frame.offset == 0; } -/* This function generates the assembly code for function exit, - on machines that need it. +/* Emit RTL for the "epilogue" or "sibcall_epilogue" define_expand; + SIBCALL_P says which. 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. */ -#ifdef CRDS - -static void -m68k_output_function_epilogue (stream, size) - FILE *stream; - HOST_WIDE_INT size; +void +m68k_expand_epilogue (bool sibcall_p) { - register int regno; - register int mask, fmask; - register int nregs; - HOST_WIDE_INT offset, foffset, fpoffset; - HOST_WIDE_INT fsize = ((size) + 3) & -4; - int big = 0; - - nregs = 0; fmask = 0; fpoffset = 0; - for (regno = 16; regno < 24; regno++) - if (regs_ever_live[regno] && ! call_used_regs[regno]) - { - nregs++; - fmask |= 1 << (23 - regno); - } + HOST_WIDE_INT fsize, fsize_with_regs; + bool big, restore_from_sp; - foffset = fpoffset + nregs * 12; - nregs = 0; mask = 0; - if (frame_pointer_needed) - regs_ever_live[FRAME_POINTER_REGNUM] = 0; + m68k_compute_frame_layout (); - for (regno = 0; regno < 16; regno++) - if (regs_ever_live[regno] && ! call_used_regs[regno]) - { - nregs++; - mask |= 1 << regno; - } + fsize = current_frame.size; + big = false; + restore_from_sp = false; - offset = foffset + nregs * 4; - if (offset + fsize >= 0x8000 - && frame_pointer_needed - && (mask || fmask || fpoffset)) + /* 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 + || (!cfun->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; + if (TARGET_COLDFIRE && restore_from_sp) { - fprintf (stream, "\tmovel $%d,a0\n", -fsize); - fsize = 0, big = 1; + /* 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 (exact_log2 (mask) >= 0) + if (current_frame.offset + fsize >= 0x8000 + && !restore_from_sp + && (current_frame.reg_mask || current_frame.fpu_mask)) { - if (big) - fprintf (stream, "\tmovel -%d(a6,a0.l),%s\n", - offset + fsize, reg_names[exact_log2 (mask)]); - else if (! frame_pointer_needed) - fprintf (stream, "\tmovel (sp)+,%s\n", - reg_names[exact_log2 (mask)]); + 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 - fprintf (stream, "\tmovel -%d(a6),%s\n", - offset + fsize, reg_names[exact_log2 (mask)]); + { + emit_move_insn (gen_rtx_REG (Pmode, A1_REG), GEN_INT (-fsize)); + fsize = 0; + big = true; + } } - else if (mask) + + if (current_frame.reg_no < MIN_MOVEM_REGS) { - if (big) - fprintf (stream, "\tmovem -%d(a6,a0.l),$0x%x\n", - offset + fsize, mask); - else if (! frame_pointer_needed) - fprintf (stream, "\tmovem (sp)+,$0x%x\n", mask); - else - fprintf (stream, "\tmovem -%d(a6),$0x%x\n", - offset + fsize, mask); - } + /* Restore each register separately in the same order moveml does. */ + int i; + HOST_WIDE_INT offset; + + offset = current_frame.offset + fsize; + for (i = 0; i < 16; i++) + if (current_frame.reg_mask & (1 << i)) + { + rtx addr; - if (fmask) + if (big) + { + /* 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); + } + 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) { if (big) - fprintf (stream, "\tfmovem -%d(a6,a0.l),$0x%x\n", - foffset + fsize, fmask); - else if (! frame_pointer_needed) - fprintf (stream, "\tfmovem (sp)+,$0x%x\n", fmask); + 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 - fprintf (stream, "\tfmovem -%d(a6),$0x%x\n", - foffset + fsize, fmask); + m68k_emit_movem (frame_pointer_rtx, + -(current_frame.offset + fsize), + current_frame.reg_no, D0_REG, + current_frame.reg_mask, false, false); } - if (fpoffset != 0) - for (regno = 55; regno >= 24; regno--) - if (regs_ever_live[regno] && ! call_used_regs[regno]) + if (current_frame.fpu_no > 0) + { + if (big) + 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 (big) - fprintf(stream, "\tfpmoved -%d(a6,a0.l), %s\n", - fpoffset + fsize, reg_names[regno]); - else if (! frame_pointer_needed) - fprintf(stream, "\tfpmoved (sp)+, %s\n", - reg_names[regno]); + if (TARGET_COLDFIRE) + { + int offset; + + /* 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 * GET_MODE_SIZE (SImode); + m68k_emit_movem (stack_pointer_rtx, offset, + current_frame.fpu_no, FP0_REG, + current_frame.fpu_mask, false, false); + } else - fprintf(stream, "\tfpmoved -%d(a6), %s\n", - fpoffset + fsize, reg_names[regno]); - fpoffset -= 8; + m68k_emit_movem (stack_pointer_rtx, 0, + current_frame.fpu_no, FP0_REG, + current_frame.fpu_mask, false, true); } + else + 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 a6\n"); - else if (fsize) + emit_insn (gen_unlink (frame_pointer_rtx)); + else if (fsize_with_regs) + emit_insn (gen_addsi3 (stack_pointer_rtx, + stack_pointer_rtx, + GEN_INT (fsize_with_regs))); + + if (crtl->calls_eh_return) + emit_insn (gen_addsi3 (stack_pointer_rtx, + stack_pointer_rtx, + EH_RETURN_STACKADJ_RTX)); + + if (!sibcall_p) + emit_jump_insn (gen_rtx_RETURN (VOIDmode)); +} + +/* Return true if X is a valid comparison operator for the dbcc + instruction. + + Note it rejects floating point comparison operators. + (In the future we could use Fdbcc). + + It also rejects some comparisons when CC_NO_OVERFLOW is set. */ + +int +valid_dbcc_comparison_p_2 (rtx x, enum machine_mode mode ATTRIBUTE_UNUSED) +{ + switch (GET_CODE (x)) { - if (fsize + 4 < 0x8000) - fprintf (stream, "\tadd.w #%d,sp\n", fsize + 4); - else - fprintf (stream, "\tadd.l #%d,sp\n", fsize + 4); + case EQ: case NE: case GTU: case LTU: + case GEU: case LEU: + return 1; + + /* Reject some when CC_NO_OVERFLOW is set. This may be over + conservative */ + case GT: case LT: case GE: case LE: + return ! (cc_prev_status.flags & CC_NO_OVERFLOW); + default: + return 0; } +} - if (current_function_pops_args) - fprintf (stream, "\trtd $%d\n", current_function_pops_args); - else - fprintf (stream, "\trts\n"); +/* Return nonzero if flags are currently in the 68881 flag register. */ +int +flags_in_68881 (void) +{ + /* We could add support for these in the future */ + return cc_status.flags & CC_IN_68881; } -#else -#if defined (DPX2) && defined (MOTOROLA) +/* Implement TARGET_FUNCTION_OK_FOR_SIBCALL_P. */ -static void -m68k_output_function_epilogue (stream, size) - FILE *stream; - HOST_WIDE_INT size; +static bool +m68k_ok_for_sibcall_p (tree decl, tree exp) { - register int regno; - register int mask, fmask; - register int nregs; - HOST_WIDE_INT offset, foffset, fpoffset, first = 1; - HOST_WIDE_INT fsize = ((size) + 3) & -4; - int big = 0; - rtx insn = get_last_insn (); - - /* If the last insn was a BARRIER, we don't have to write any code. */ - if (GET_CODE (insn) == NOTE) - insn = prev_nonnote_insn (insn); - if (insn && GET_CODE (insn) == BARRIER) - { - /* Output just a no-op so that debuggers don't get confused - about which function the pc is in at this address. */ - fprintf (stream, "\tnop\n"); - return; - } + enum m68k_function_kind kind; + + /* We cannot use sibcalls for nested functions because we use the + static chain register for indirect calls. */ + if (CALL_EXPR_STATIC_CHAIN (exp)) + return false; + + kind = m68k_get_function_kind (current_function_decl); + if (kind == m68k_fk_normal_function) + /* We can always sibcall from a normal function, because it's + undefined if it is calling an interrupt function. */ + return true; + + /* Otherwise we can only sibcall if the function kind is known to be + the same. */ + if (decl && m68k_get_function_kind (decl) == kind) + return true; + + return false; +} - nregs = 0; fmask = 0; fpoffset = 0; - for (regno = 16; regno < 24; regno++) - if (regs_ever_live[regno] && ! call_used_regs[regno]) - { - nregs++; - fmask |= 1 << (23 - regno); - } +/* Convert X to a legitimate function call memory reference and return the + result. */ - foffset = fpoffset + nregs * 12; - nregs = 0; mask = 0; - if (frame_pointer_needed) - regs_ever_live[FRAME_POINTER_REGNUM] = 0; +rtx +m68k_legitimize_call_address (rtx x) +{ + gcc_assert (MEM_P (x)); + if (call_operand (XEXP (x, 0), VOIDmode)) + return x; + return replace_equiv_address (x, force_reg (Pmode, XEXP (x, 0))); +} - for (regno = 0; regno < 16; regno++) - if (regs_ever_live[regno] && ! call_used_regs[regno]) - { - nregs++; - mask |= 1 << regno; - } +/* Likewise for sibling calls. */ - offset = foffset + nregs * 4; - if (offset + fsize >= 0x8000 - && frame_pointer_needed - && (mask || fmask || fpoffset)) - { - fprintf (stream, "\tmove.l #%d,a0\n", -fsize); - fsize = 0, big = 1; - } +rtx +m68k_legitimize_sibcall_address (rtx x) +{ + gcc_assert (MEM_P (x)); + if (sibcall_operand (XEXP (x, 0), VOIDmode)) + return x; + + emit_move_insn (gen_rtx_REG (Pmode, STATIC_CHAIN_REGNUM), XEXP (x, 0)); + return replace_equiv_address (x, gen_rtx_REG (Pmode, STATIC_CHAIN_REGNUM)); +} + +/* Output a dbCC; jCC sequence. Note we do not handle the + floating point version of this sequence (Fdbcc). We also + do not handle alternative conditions when CC_NO_OVERFLOW is + set. It is assumed that valid_dbcc_comparison_p and flags_in_68881 will + kick those out before we get here. */ - if (nregs <= 2) +void +output_dbcc_and_branch (rtx *operands) +{ + switch (GET_CODE (operands[3])) { - /* 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. */ + case EQ: + output_asm_insn ("dbeq %0,%l1\n\tjeq %l2", operands); + break; - int i; + case NE: + output_asm_insn ("dbne %0,%l1\n\tjne %l2", operands); + break; - /* Undo the work from above. */ - for (i = 0; i< 16; i++) - if (mask & (1 << i)) - { - if (big) - fprintf (stream, "\tmove.l -%d(%s,a0.l),%s\n", - offset + fsize, - reg_names[FRAME_POINTER_REGNUM], - reg_names[i]); - else if (! frame_pointer_needed) - fprintf (stream, "\tmove.l (sp)+,%s\n", - reg_names[i]); - else - fprintf (stream, "\tmove.l -%d(%s),%s\n", - offset + fsize, - reg_names[FRAME_POINTER_REGNUM], - reg_names[i]); - offset = offset - 4; - } - } - else if (mask) - { - first = 1; - for (regno = 0; regno < 16; regno++) - if (mask & (1 << regno)) - { - if (first && big) - { - fprintf (stream, "\tmovem.l -%d(%s,a0.l),%s", - offset + fsize, - reg_names[FRAME_POINTER_REGNUM], - reg_names[regno]); - first = 0; - } - else if (first && ! frame_pointer_needed) - { - fprintf (stream, "\tmovem.l (sp)+,%s", - reg_names[regno]); - first = 0; - } - else if (first) - { - fprintf (stream, "\tmovem.l -%d(%s),%s", - offset + fsize, - reg_names[FRAME_POINTER_REGNUM], - reg_names[regno]); - first = 0; - } - else - fprintf (stream, "/%s", reg_names[regno]); - } - fprintf (stream, "\n"); - } + case GT: + output_asm_insn ("dbgt %0,%l1\n\tjgt %l2", operands); + break; - if (fmask) - { - first = 1; - for (regno = 16; regno < 24; regno++) - if (fmask & (1 << (23 - regno))) - { - if (first && big) - { - fprintf (stream, "\tfmovem.x -%d(%s,a0.l),%s", - foffset + fsize, - reg_names[FRAME_POINTER_REGNUM], - reg_names[regno]); - first = 0; - } - else if (first && ! frame_pointer_needed) - { - fprintf (stream, "\tfmovem.x (sp)+,%s", - reg_names[regno]); - first = 0; - } - else if (first) - { - fprintf (stream, "\tfmovem.x -%d(%s),%s", - foffset + fsize, - reg_names[FRAME_POINTER_REGNUM], - reg_names[regno]); - first = 0; - } - else - fprintf (stream, "/%s", reg_names[regno]); - } - fprintf (stream, "\n"); - } + case GTU: + output_asm_insn ("dbhi %0,%l1\n\tjhi %l2", operands); + break; - if (frame_pointer_needed) - fprintf (stream, "\tunlk %s\n", - reg_names[FRAME_POINTER_REGNUM]); - else if (fsize) - { - if (fsize + 4 < 0x8000) - fprintf (stream, "\tadd.w #%d,sp\n", fsize + 4); - else - fprintf (stream, "\tadd.l #%d,sp\n", fsize + 4); - } + case LT: + output_asm_insn ("dblt %0,%l1\n\tjlt %l2", operands); + break; - if (current_function_pops_args) - fprintf (stream, "\trtd #%d\n", current_function_pops_args); - else - fprintf (stream, "\trts\n"); -} + case LTU: + output_asm_insn ("dbcs %0,%l1\n\tjcs %l2", operands); + break; -#else -#if defined (NEWS) && defined (MOTOROLA) + case GE: + output_asm_insn ("dbge %0,%l1\n\tjge %l2", operands); + break; -static void -m68k_output_function_epilogue (stream, size) - FILE *stream; - HOST_WIDE_INT size; -{ - register int regno; - register int mask, fmask; - register int nregs; - HOST_WIDE_INT offset, foffset; - HOST_WIDE_INT fsize = ((size) + 3) & -4; - int big = 0; - - nregs = 0; fmask = 0; - for (regno = 16; regno < FIRST_PSEUDO_REGISTER; regno++) - if (regs_ever_live[regno] && ! call_used_regs[regno]) - { - nregs++; - fmask |= 1 << (23 - regno); - } + case GEU: + output_asm_insn ("dbcc %0,%l1\n\tjcc %l2", operands); + break; - foffset = nregs * 12; - nregs = 0; mask = 0; - if (frame_pointer_needed) - regs_ever_live[FRAME_POINTER_REGNUM] = 0; + case LE: + output_asm_insn ("dble %0,%l1\n\tjle %l2", operands); + break; - for (regno = 0; regno < 16; regno++) - if (regs_ever_live[regno] && ! call_used_regs[regno]) - { - nregs++; - mask |= 1 << regno; - } + case LEU: + output_asm_insn ("dbls %0,%l1\n\tjls %l2", operands); + break; - offset = foffset + nregs * 4; - if (offset + fsize >= 0x8000 - && frame_pointer_needed - && (mask || fmask)) - { - fprintf (stream, "\tmove.l #%d,a0\n", -fsize); - fsize = 0, big = 1; + default: + gcc_unreachable (); } - if (exact_log2 (mask) >= 0) - { - if (big) - fprintf (stream, "\tmove.l (-%d,fp,a0.l),%s\n", - offset + fsize, reg_names[exact_log2 (mask)]); - else if (! frame_pointer_needed) - fprintf (stream, "\tmove.l (sp)+,%s\n", - reg_names[exact_log2 (mask)]); - else - fprintf (stream, "\tmove.l (-%d,fp),%s\n", - offset + fsize, reg_names[exact_log2 (mask)]); - } - else if (mask) + /* If the decrement is to be done in SImode, then we have + to compensate for the fact that dbcc decrements in HImode. */ + switch (GET_MODE (operands[0])) { - if (big) - fprintf (stream, "\tmovem.l (-%d,fp,a0.l),#0x%x\n", - offset + fsize, mask); - else if (! frame_pointer_needed) - fprintf (stream, "\tmovem.l (sp)+,#0x%x\n", mask); - else - fprintf (stream, "\tmovem.l (-%d,fp),#0x%x\n", - offset + fsize, mask); + case SImode: + output_asm_insn ("clr%.w %0\n\tsubq%.l #1,%0\n\tjpl %l1", operands); + break; + + case HImode: + break; + + default: + gcc_unreachable (); } - - if (fmask) - { - if (big) - fprintf (stream, "\tfmovem.x (-%d,fp,a0.l),#0x%x\n", - foffset + fsize, fmask); - else if (! frame_pointer_needed) - fprintf (stream, "\tfmovem.x (sp)+,#0x%x\n", fmask); - else - fprintf (stream, "\tfmovem.x (-%d,fp),#0x%x\n", - foffset + fsize, fmask); - } - - if (frame_pointer_needed) - fprintf (stream, "\tunlk fp\n"); - else if (fsize) - { - if (fsize + 4 < 0x8000) - fprintf (stream, "\tadd.w #%d,sp\n", fsize + 4); - else - fprintf (stream, "\tadd.l #%d,sp\n", fsize + 4); - } - - if (current_function_pops_args) - fprintf (stream, "\trtd #%d\n", current_function_pops_args); - else - fprintf (stream, "\trts\n"); -} - -#else /* !CRDS && ! (NEWS && MOTOROLA) && ! (DPX2 && MOTOROLA) */ - -static void -m68k_output_function_epilogue (stream, size) - FILE *stream; - HOST_WIDE_INT size; -{ - register int regno; - register int mask, fmask; - register int nregs; - HOST_WIDE_INT offset, foffset, fpoffset; - HOST_WIDE_INT fsize = (size + 3) & -4; - int big = 0; - rtx insn = get_last_insn (); - int restore_from_sp = 0; - - /* If the last insn was a BARRIER, we don't have to write any code. */ - if (GET_CODE (insn) == NOTE) - insn = prev_nonnote_insn (insn); - if (insn && GET_CODE (insn) == BARRIER) - { - /* Output just a no-op so that debuggers don't get confused - about which function the pc is in at this address. */ - asm_fprintf (stream, "\tnop\n"); - return; - } - -#ifdef FUNCTION_EXTRA_EPILOGUE - FUNCTION_EXTRA_EPILOGUE (stream, size); -#endif - nregs = 0; fmask = 0; fpoffset = 0; -#ifdef SUPPORT_SUN_FPA - for (regno = 24 ; regno < 56 ; regno++) - if (regs_ever_live[regno] && ! call_used_regs[regno]) - nregs++; - fpoffset = nregs * 8; -#endif - nregs = 0; - if (TARGET_68881) - { - for (regno = 16; regno < 24; regno++) - if (regs_ever_live[regno] && ! call_used_regs[regno]) - { - nregs++; - fmask |= 1 << (23 - regno); - } - } - foffset = fpoffset + nregs * 12; - nregs = 0; mask = 0; - if (frame_pointer_needed) - regs_ever_live[FRAME_POINTER_REGNUM] = 0; - for (regno = 0; regno < 16; regno++) - if (regs_ever_live[regno] && ! call_used_regs[regno]) - { - nregs++; - mask |= 1 << regno; - } - if (flag_pic && current_function_uses_pic_offset_table) - { - nregs++; - mask |= 1 << PIC_OFFSET_TABLE_REGNUM; - } - offset = foffset + nregs * 4; - /* FIXME : leaf_function_p below is too strong. - What we really need to know there is if there could be pending - stack adjustment needed at that point. */ - restore_from_sp = ! frame_pointer_needed - || (! current_function_calls_alloca && leaf_function_p ()); - if (offset + fsize >= 0x8000 - && ! restore_from_sp - && (mask || fmask || fpoffset)) - { -#ifdef MOTOROLA - asm_fprintf (stream, "\t%Omove.l %0I%d,%Ra1\n", -fsize); -#else - asm_fprintf (stream, "\tmovel %0I%d,%Ra1\n", -fsize); -#endif - fsize = 0, big = 1; - } - if (TARGET_5200 || nregs <= 2) - { - /* Restore each separately in the same order moveml does. - Using two movel instructions instead of a single moveml - is about 15% faster for the 68020 and 68030 at no expense - in code size. */ - - int i; - - /* Undo the work from above. */ - for (i = 0; i< 16; i++) - if (mask & (1 << i)) - { - if (big) - { -#ifdef MOTOROLA - asm_fprintf (stream, "\t%Omove.l -%d(%s,%Ra1.l),%s\n", - offset + fsize, - reg_names[FRAME_POINTER_REGNUM], - reg_names[i]); -#else - asm_fprintf (stream, "\tmovel %s@(-%d,%Ra1:l),%s\n", - reg_names[FRAME_POINTER_REGNUM], - offset + fsize, reg_names[i]); -#endif - } - else if (restore_from_sp) - { -#ifdef MOTOROLA - asm_fprintf (stream, "\t%Omove.l (%Rsp)+,%s\n", - reg_names[i]); -#else - asm_fprintf (stream, "\tmovel %Rsp@+,%s\n", - reg_names[i]); -#endif - } - else - { -#ifdef MOTOROLA - asm_fprintf (stream, "\t%Omove.l -%d(%s),%s\n", - offset + fsize, - reg_names[FRAME_POINTER_REGNUM], - reg_names[i]); -#else - asm_fprintf (stream, "\tmovel %s@(-%d),%s\n", - reg_names[FRAME_POINTER_REGNUM], - offset + fsize, reg_names[i]); -#endif - } - offset = offset - 4; - } - } - else if (mask) - { - if (big) - { -#ifdef MOTOROLA - asm_fprintf (stream, "\tmovm.l -%d(%s,%Ra1.l),%0I0x%x\n", - offset + fsize, - reg_names[FRAME_POINTER_REGNUM], - mask); -#else - asm_fprintf (stream, "\tmoveml %s@(-%d,%Ra1:l),%0I0x%x\n", - reg_names[FRAME_POINTER_REGNUM], - offset + fsize, mask); -#endif - } - else if (restore_from_sp) - { -#ifdef MOTOROLA - asm_fprintf (stream, "\tmovm.l (%Rsp)+,%0I0x%x\n", mask); -#else - asm_fprintf (stream, "\tmoveml %Rsp@+,%0I0x%x\n", mask); -#endif - } - else - { -#ifdef MOTOROLA - asm_fprintf (stream, "\tmovm.l -%d(%s),%0I0x%x\n", - offset + fsize, - reg_names[FRAME_POINTER_REGNUM], - mask); -#else - asm_fprintf (stream, "\tmoveml %s@(-%d),%0I0x%x\n", - reg_names[FRAME_POINTER_REGNUM], - offset + fsize, mask); -#endif - } - } - if (fmask) - { - if (big) - { -#ifdef MOTOROLA - asm_fprintf (stream, "\tfmovm -%d(%s,%Ra1.l),%0I0x%x\n", - foffset + fsize, - reg_names[FRAME_POINTER_REGNUM], - fmask); -#else - asm_fprintf (stream, "\tfmovem %s@(-%d,%Ra1:l),%0I0x%x\n", - reg_names[FRAME_POINTER_REGNUM], - foffset + fsize, fmask); -#endif - } - else if (restore_from_sp) - { -#ifdef MOTOROLA - asm_fprintf (stream, "\tfmovm (%Rsp)+,%0I0x%x\n", fmask); -#else - asm_fprintf (stream, "\tfmovem %Rsp@+,%0I0x%x\n", fmask); -#endif - } - else - { -#ifdef MOTOROLA - asm_fprintf (stream, "\tfmovm -%d(%s),%0I0x%x\n", - foffset + fsize, - reg_names[FRAME_POINTER_REGNUM], - fmask); -#else - asm_fprintf (stream, "\tfmovem %s@(-%d),%0I0x%x\n", - reg_names[FRAME_POINTER_REGNUM], - foffset + fsize, fmask); -#endif - } - } - if (fpoffset != 0) - for (regno = 55; regno >= 24; regno--) - if (regs_ever_live[regno] && ! call_used_regs[regno]) - { - if (big) - { -#ifdef MOTOROLA - asm_fprintf (stream, "\tfpmovd -%d(%s,%Ra1.l), %s\n", - fpoffset + fsize, - reg_names[FRAME_POINTER_REGNUM], - reg_names[regno]); -#else - asm_fprintf (stream, "\tfpmoved %s@(-%d,%Ra1:l), %s\n", - reg_names[FRAME_POINTER_REGNUM], - fpoffset + fsize, reg_names[regno]); -#endif - } - else if (restore_from_sp) - { -#ifdef MOTOROLA - asm_fprintf (stream, "\tfpmovd (%Rsp)+,%s\n", - reg_names[regno]); -#else - asm_fprintf (stream, "\tfpmoved %Rsp@+, %s\n", - reg_names[regno]); -#endif - } - else - { -#ifdef MOTOROLA - asm_fprintf (stream, "\tfpmovd -%d(%s), %s\n", - fpoffset + fsize, - reg_names[FRAME_POINTER_REGNUM], - reg_names[regno]); -#else - asm_fprintf (stream, "\tfpmoved %s@(-%d), %s\n", - reg_names[FRAME_POINTER_REGNUM], - fpoffset + fsize, reg_names[regno]); -#endif - } - fpoffset -= 8; - } - if (frame_pointer_needed) - fprintf (stream, "\tunlk %s\n", - reg_names[FRAME_POINTER_REGNUM]); - else if (fsize) - { -#ifndef NO_ADDSUB_Q - if (fsize + 4 <= 8) - { - if (!TARGET_5200) - { -#ifdef MOTOROLA - asm_fprintf (stream, "\taddq.w %0I%d,%Rsp\n", fsize + 4); -#else - asm_fprintf (stream, "\taddqw %0I%d,%Rsp\n", fsize + 4); -#endif - } - else - { -#ifdef MOTOROLA - asm_fprintf (stream, "\taddq.l %0I%d,%Rsp\n", fsize + 4); -#else - asm_fprintf (stream, "\taddql %0I%d,%Rsp\n", fsize + 4); -#endif - } - } - else if (fsize + 4 <= 16 && TARGET_CPU32) - { - /* On the CPU32 it is faster to use two addqw instructions to - add a small integer (8 < N <= 16) to a register. */ - /* asm_fprintf() cannot handle %. */ -#ifdef MOTOROLA - asm_fprintf (stream, "\taddq.w %0I8,%Rsp\n\taddq.w %0I%d,%Rsp\n", - fsize + 4 - 8); -#else - asm_fprintf (stream, "\taddqw %0I8,%Rsp\n\taddqw %0I%d,%Rsp\n", - fsize + 4 - 8); -#endif - } - else -#endif /* not NO_ADDSUB_Q */ - if (fsize + 4 < 0x8000) - { - if (TARGET_68040) - { - /* asm_fprintf() cannot handle %. */ -#ifdef MOTOROLA - asm_fprintf (stream, "\tadd.w %0I%d,%Rsp\n", fsize + 4); -#else - asm_fprintf (stream, "\taddw %0I%d,%Rsp\n", fsize + 4); -#endif - } - else - { -#ifdef MOTOROLA - asm_fprintf (stream, "\tlea (%d,%Rsp),%Rsp\n", fsize + 4); -#else - asm_fprintf (stream, "\tlea %Rsp@(%d),%Rsp\n", fsize + 4); -#endif - } - } - else - { - /* asm_fprintf() cannot handle %. */ -#ifdef MOTOROLA - asm_fprintf (stream, "\tadd.l %0I%d,%Rsp\n", fsize + 4); -#else - asm_fprintf (stream, "\taddl %0I%d,%Rsp\n", fsize + 4); -#endif - } - } - if (current_function_pops_args) - asm_fprintf (stream, "\trtd %0I%d\n", current_function_pops_args); - else - fprintf (stream, "\trts\n"); -} - -#endif /* ! (DPX2 && MOTOROLA) */ -#endif /* ! (NEWS && MOTOROLA) */ -#endif /* !CRDS */ - -/* Similar to general_operand, but exclude stack_pointer_rtx. */ - -int -not_sp_operand (op, mode) - register rtx op; - enum machine_mode mode; -{ - return op != stack_pointer_rtx && nonimmediate_operand (op, mode); -} - -/* Return TRUE if X is a valid comparison operator for the dbcc - instruction. - - Note it rejects floating point comparison operators. - (In the future we could use Fdbcc). - - It also rejects some comparisons when CC_NO_OVERFLOW is set. */ - -int -valid_dbcc_comparison_p (x, mode) - rtx x; - enum machine_mode mode ATTRIBUTE_UNUSED; -{ - switch (GET_CODE (x)) - { - case EQ: case NE: case GTU: case LTU: - case GEU: case LEU: - return 1; - - /* Reject some when CC_NO_OVERFLOW is set. This may be over - conservative */ - case GT: case LT: case GE: case LE: - return ! (cc_prev_status.flags & CC_NO_OVERFLOW); - default: - return 0; - } -} - -/* Return non-zero if flags are currently in the 68881 flag register. */ -int -flags_in_68881 () -{ - /* We could add support for these in the future */ - return cc_status.flags & CC_IN_68881; -} - -/* Output a dbCC; jCC sequence. Note we do not handle the - floating point version of this sequence (Fdbcc). We also - do not handle alternative conditions when CC_NO_OVERFLOW is - set. It is assumed that valid_dbcc_comparison_p and flags_in_68881 will - kick those out before we get here. */ - -void -output_dbcc_and_branch (operands) - rtx *operands; -{ - switch (GET_CODE (operands[3])) - { - case EQ: -#ifdef MOTOROLA - output_asm_insn ("dbeq %0,%l1\n\tjbeq %l2", operands); -#else - output_asm_insn ("dbeq %0,%l1\n\tjeq %l2", operands); -#endif - break; - - case NE: -#ifdef MOTOROLA - output_asm_insn ("dbne %0,%l1\n\tjbne %l2", operands); -#else - output_asm_insn ("dbne %0,%l1\n\tjne %l2", operands); -#endif - break; - - case GT: -#ifdef MOTOROLA - output_asm_insn ("dbgt %0,%l1\n\tjbgt %l2", operands); -#else - output_asm_insn ("dbgt %0,%l1\n\tjgt %l2", operands); -#endif - break; - - case GTU: -#ifdef MOTOROLA - output_asm_insn ("dbhi %0,%l1\n\tjbhi %l2", operands); -#else - output_asm_insn ("dbhi %0,%l1\n\tjhi %l2", operands); -#endif - break; - - case LT: -#ifdef MOTOROLA - output_asm_insn ("dblt %0,%l1\n\tjblt %l2", operands); -#else - output_asm_insn ("dblt %0,%l1\n\tjlt %l2", operands); -#endif - break; - - case LTU: -#ifdef MOTOROLA - output_asm_insn ("dbcs %0,%l1\n\tjbcs %l2", operands); -#else - output_asm_insn ("dbcs %0,%l1\n\tjcs %l2", operands); -#endif - break; - - case GE: -#ifdef MOTOROLA - output_asm_insn ("dbge %0,%l1\n\tjbge %l2", operands); -#else - output_asm_insn ("dbge %0,%l1\n\tjge %l2", operands); -#endif - break; - - case GEU: -#ifdef MOTOROLA - output_asm_insn ("dbcc %0,%l1\n\tjbcc %l2", operands); -#else - output_asm_insn ("dbcc %0,%l1\n\tjcc %l2", operands); -#endif - break; - - case LE: -#ifdef MOTOROLA - output_asm_insn ("dble %0,%l1\n\tjble %l2", operands); -#else - output_asm_insn ("dble %0,%l1\n\tjle %l2", operands); -#endif - break; - - case LEU: -#ifdef MOTOROLA - output_asm_insn ("dbls %0,%l1\n\tjbls %l2", operands); -#else - output_asm_insn ("dbls %0,%l1\n\tjls %l2", operands); -#endif - break; - - default: - abort (); - } - - /* If the decrement is to be done in SImode, then we have - to compensate for the fact that dbcc decrements in HImode. */ - switch (GET_MODE (operands[0])) - { - case SImode: -#ifdef MOTOROLA - output_asm_insn ("clr%.w %0\n\tsubq%.l %#1,%0\n\tjbpl %l1", operands); -#else - output_asm_insn ("clr%.w %0\n\tsubq%.l %#1,%0\n\tjpl %l1", operands); -#endif - break; - - case HImode: - break; - - default: - abort (); - } -} +} const char * -output_scc_di(op, operand1, operand2, dest) - rtx op; - rtx operand1; - rtx operand2; - rtx dest; +output_scc_di (rtx op, rtx operand1, rtx operand2, rtx dest) { rtx loperands[7]; enum rtx_code op_code = GET_CODE (op); @@ -1705,162 +1529,112 @@ output_scc_di(op, operand1, operand2, dest) else loperands[3] = adjust_address (operand2, SImode, 4); } - loperands[4] = gen_label_rtx(); + loperands[4] = gen_label_rtx (); if (operand2 != const0_rtx) - { -#ifdef MOTOROLA -#ifdef SGS_CMP_ORDER - output_asm_insn ("cmp%.l %0,%2\n\tjbne %l4\n\tcmp%.l %1,%3", loperands); -#else - output_asm_insn ("cmp%.l %2,%0\n\tjbne %l4\n\tcmp%.l %3,%1", loperands); -#endif -#else -#ifdef SGS_CMP_ORDER - output_asm_insn ("cmp%.l %0,%2\n\tjne %l4\n\tcmp%.l %1,%3", loperands); -#else - output_asm_insn ("cmp%.l %2,%0\n\tjne %l4\n\tcmp%.l %3,%1", loperands); -#endif -#endif - } + output_asm_insn ("cmp%.l %2,%0\n\tjne %l4\n\tcmp%.l %3,%1", loperands); else { - if (TARGET_68020 || TARGET_5200 || ! ADDRESS_REG_P (loperands[0])) + if (TARGET_68020 || TARGET_COLDFIRE || ! ADDRESS_REG_P (loperands[0])) output_asm_insn ("tst%.l %0", loperands); else - { -#ifdef SGS_CMP_ORDER - output_asm_insn ("cmp%.w %0,%#0", loperands); -#else - output_asm_insn ("cmp%.w %#0,%0", loperands); -#endif - } + output_asm_insn ("cmp%.w #0,%0", loperands); -#ifdef MOTOROLA - output_asm_insn ("jbne %l4", loperands); -#else output_asm_insn ("jne %l4", loperands); -#endif - if (TARGET_68020 || TARGET_5200 || ! ADDRESS_REG_P (loperands[1])) + if (TARGET_68020 || TARGET_COLDFIRE || ! ADDRESS_REG_P (loperands[1])) output_asm_insn ("tst%.l %1", loperands); else - { -#ifdef SGS_CMP_ORDER - output_asm_insn ("cmp%.w %1,%#0", loperands); -#else - output_asm_insn ("cmp%.w %#0,%1", loperands); -#endif - } + output_asm_insn ("cmp%.w #0,%1", loperands); } loperands[5] = dest; - + switch (op_code) { case EQ: - ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "L", - CODE_LABEL_NUMBER (loperands[4])); + (*targetm.asm_out.internal_label) (asm_out_file, "L", + CODE_LABEL_NUMBER (loperands[4])); output_asm_insn ("seq %5", loperands); break; case NE: - ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "L", - CODE_LABEL_NUMBER (loperands[4])); + (*targetm.asm_out.internal_label) (asm_out_file, "L", + CODE_LABEL_NUMBER (loperands[4])); output_asm_insn ("sne %5", loperands); break; case GT: - loperands[6] = gen_label_rtx(); -#ifdef MOTOROLA - output_asm_insn ("shi %5\n\tjbra %l6", loperands); -#else + loperands[6] = gen_label_rtx (); output_asm_insn ("shi %5\n\tjra %l6", loperands); -#endif - ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "L", - CODE_LABEL_NUMBER (loperands[4])); + (*targetm.asm_out.internal_label) (asm_out_file, "L", + CODE_LABEL_NUMBER (loperands[4])); output_asm_insn ("sgt %5", loperands); - ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "L", - CODE_LABEL_NUMBER (loperands[6])); + (*targetm.asm_out.internal_label) (asm_out_file, "L", + CODE_LABEL_NUMBER (loperands[6])); break; case GTU: - ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "L", - CODE_LABEL_NUMBER (loperands[4])); + (*targetm.asm_out.internal_label) (asm_out_file, "L", + CODE_LABEL_NUMBER (loperands[4])); output_asm_insn ("shi %5", loperands); break; case LT: - loperands[6] = gen_label_rtx(); -#ifdef MOTOROLA - output_asm_insn ("scs %5\n\tjbra %l6", loperands); -#else + loperands[6] = gen_label_rtx (); output_asm_insn ("scs %5\n\tjra %l6", loperands); -#endif - ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "L", - CODE_LABEL_NUMBER (loperands[4])); + (*targetm.asm_out.internal_label) (asm_out_file, "L", + CODE_LABEL_NUMBER (loperands[4])); output_asm_insn ("slt %5", loperands); - ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "L", - CODE_LABEL_NUMBER (loperands[6])); + (*targetm.asm_out.internal_label) (asm_out_file, "L", + CODE_LABEL_NUMBER (loperands[6])); break; case LTU: - ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "L", - CODE_LABEL_NUMBER (loperands[4])); + (*targetm.asm_out.internal_label) (asm_out_file, "L", + CODE_LABEL_NUMBER (loperands[4])); output_asm_insn ("scs %5", loperands); break; case GE: - loperands[6] = gen_label_rtx(); -#ifdef MOTOROLA - output_asm_insn ("scc %5\n\tjbra %l6", loperands); -#else + loperands[6] = gen_label_rtx (); output_asm_insn ("scc %5\n\tjra %l6", loperands); -#endif - ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "L", - CODE_LABEL_NUMBER (loperands[4])); + (*targetm.asm_out.internal_label) (asm_out_file, "L", + CODE_LABEL_NUMBER (loperands[4])); output_asm_insn ("sge %5", loperands); - ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "L", - CODE_LABEL_NUMBER (loperands[6])); + (*targetm.asm_out.internal_label) (asm_out_file, "L", + CODE_LABEL_NUMBER (loperands[6])); break; case GEU: - ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "L", - CODE_LABEL_NUMBER (loperands[4])); + (*targetm.asm_out.internal_label) (asm_out_file, "L", + CODE_LABEL_NUMBER (loperands[4])); output_asm_insn ("scc %5", loperands); break; case LE: - loperands[6] = gen_label_rtx(); -#ifdef MOTOROLA - output_asm_insn ("sls %5\n\tjbra %l6", loperands); -#else + loperands[6] = gen_label_rtx (); output_asm_insn ("sls %5\n\tjra %l6", loperands); -#endif - ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "L", - CODE_LABEL_NUMBER (loperands[4])); + (*targetm.asm_out.internal_label) (asm_out_file, "L", + CODE_LABEL_NUMBER (loperands[4])); output_asm_insn ("sle %5", loperands); - ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "L", - CODE_LABEL_NUMBER (loperands[6])); + (*targetm.asm_out.internal_label) (asm_out_file, "L", + CODE_LABEL_NUMBER (loperands[6])); break; case LEU: - ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "L", - CODE_LABEL_NUMBER (loperands[4])); + (*targetm.asm_out.internal_label) (asm_out_file, "L", + CODE_LABEL_NUMBER (loperands[4])); output_asm_insn ("sls %5", loperands); break; default: - abort (); + gcc_unreachable (); } return ""; } const char * -output_btst (operands, countop, dataop, insn, signpos) - rtx *operands; - rtx countop, dataop; - rtx insn; - int signpos; +output_btst (rtx *operands, rtx countop, rtx dataop, rtx insn, int signpos) { operands[0] = countop; operands[1] = dataop; @@ -1892,63 +1666,365 @@ output_btst (operands, countop, dataop, insn, signpos) if (count == 7 && next_insn_tests_no_inequality (insn)) return "tst%.b %1"; + /* Try to use `movew to ccr' followed by the appropriate branch insn. + On some m68k variants unfortunately that's slower than btst. + On 68000 and higher, that should also work for all HImode operands. */ + if (TUNE_CPU32 || TARGET_COLDFIRE || optimize_size) + { + if (count == 3 && DATA_REG_P (operands[1]) + && next_insn_tests_no_inequality (insn)) + { + cc_status.flags = CC_NOT_NEGATIVE | CC_Z_IN_NOT_N | CC_NO_OVERFLOW; + return "move%.w %1,%%ccr"; + } + if (count == 2 && DATA_REG_P (operands[1]) + && next_insn_tests_no_inequality (insn)) + { + cc_status.flags = CC_NOT_NEGATIVE | CC_INVERTED | CC_NO_OVERFLOW; + return "move%.w %1,%%ccr"; + } + /* count == 1 followed by bvc/bvs and + count == 0 followed by bcc/bcs are also possible, but need + m68k-specific CC_Z_IN_NOT_V and CC_Z_IN_NOT_C flags. */ + } cc_status.flags = CC_NOT_NEGATIVE; } return "btst %0,%1"; } -/* Returns 1 if OP is either a symbol reference or a sum of a symbol - reference and a constant. */ +/* Return true if X is a legitimate base register. STRICT_P says + whether we need strict checking. */ -int -symbolic_operand (op, mode) - register rtx op; - enum machine_mode mode ATTRIBUTE_UNUSED; +bool +m68k_legitimate_base_reg_p (rtx x, bool strict_p) { - switch (GET_CODE (op)) - { - case SYMBOL_REF: - case LABEL_REF: - return 1; + /* Allow SUBREG everywhere we allow REG. This results in better code. */ + if (!strict_p && GET_CODE (x) == SUBREG) + x = SUBREG_REG (x); + + return (REG_P (x) + && (strict_p + ? REGNO_OK_FOR_BASE_P (REGNO (x)) + : REGNO_OK_FOR_BASE_NONSTRICT_P (REGNO (x)))); +} - case CONST: - op = XEXP (op, 0); - return ((GET_CODE (XEXP (op, 0)) == SYMBOL_REF - || GET_CODE (XEXP (op, 0)) == LABEL_REF) - && GET_CODE (XEXP (op, 1)) == CONST_INT); +/* Return true if X is a legitimate index register. STRICT_P says + whether we need strict checking. */ -#if 0 /* Deleted, with corresponding change in m68k.h, - so as to fit the specs. No CONST_DOUBLE is ever symbolic. */ - case CONST_DOUBLE: - return GET_MODE (op) == mode; -#endif +bool +m68k_legitimate_index_reg_p (rtx x, bool strict_p) +{ + if (!strict_p && GET_CODE (x) == SUBREG) + x = SUBREG_REG (x); - default: - return 0; + return (REG_P (x) + && (strict_p + ? REGNO_OK_FOR_INDEX_P (REGNO (x)) + : REGNO_OK_FOR_INDEX_NONSTRICT_P (REGNO (x)))); +} + +/* Return true if X is a legitimate index expression for a (d8,An,Xn) or + (bd,An,Xn) addressing mode. Fill in the INDEX and SCALE fields of + ADDRESS if so. STRICT_P says whether we need strict checking. */ + +static bool +m68k_decompose_index (rtx x, bool strict_p, struct m68k_address *address) +{ + int scale; + + /* Check for a scale factor. */ + scale = 1; + if ((TARGET_68020 || TARGET_COLDFIRE) + && GET_CODE (x) == MULT + && GET_CODE (XEXP (x, 1)) == CONST_INT + && (INTVAL (XEXP (x, 1)) == 2 + || INTVAL (XEXP (x, 1)) == 4 + || (INTVAL (XEXP (x, 1)) == 8 + && (TARGET_COLDFIRE_FPU || !TARGET_COLDFIRE)))) + { + scale = INTVAL (XEXP (x, 1)); + x = XEXP (x, 0); + } + + /* Check for a word extension. */ + if (!TARGET_COLDFIRE + && GET_CODE (x) == SIGN_EXTEND + && GET_MODE (XEXP (x, 0)) == HImode) + x = XEXP (x, 0); + + if (m68k_legitimate_index_reg_p (x, strict_p)) + { + address->scale = scale; + address->index = x; + return true; } + + return false; } - -/* Check for sign_extend or zero_extend. Used for bit-count operands. */ -int -extend_operator(x, mode) - rtx x; - enum machine_mode mode; +/* Return true if X is an illegitimate symbolic constant. */ + +bool +m68k_illegitimate_symbolic_constant_p (rtx x) +{ + rtx base, offset; + + if (M68K_OFFSETS_MUST_BE_WITHIN_SECTIONS_P) + { + split_const (x, &base, &offset); + if (GET_CODE (base) == SYMBOL_REF + && !offset_within_block_p (base, INTVAL (offset))) + return true; + } + return false; +} + +/* Return true if X is a legitimate constant address that can reach + bytes in the range [X, X + REACH). STRICT_P says whether we need + strict checking. */ + +static bool +m68k_legitimate_constant_address_p (rtx x, unsigned int reach, bool strict_p) +{ + rtx base, offset; + + if (!CONSTANT_ADDRESS_P (x)) + return false; + + if (flag_pic + && !(strict_p && TARGET_PCREL) + && symbolic_operand (x, VOIDmode)) + return false; + + if (M68K_OFFSETS_MUST_BE_WITHIN_SECTIONS_P && reach > 1) + { + split_const (x, &base, &offset); + if (GET_CODE (base) == SYMBOL_REF + && !offset_within_block_p (base, INTVAL (offset) + reach - 1)) + return false; + } + + return true; +} + +/* Return true if X is a LABEL_REF for a jump table. Assume that unplaced + labels will become jump tables. */ + +static bool +m68k_jump_table_ref_p (rtx x) +{ + if (GET_CODE (x) != LABEL_REF) + return false; + + x = XEXP (x, 0); + if (!NEXT_INSN (x) && !PREV_INSN (x)) + return true; + + x = next_nonnote_insn (x); + return x && JUMP_TABLE_DATA_P (x); +} + +/* Return true if X is a legitimate address for values of mode MODE. + STRICT_P says whether strict checking is needed. If the address + is valid, describe its components in *ADDRESS. */ + +static bool +m68k_decompose_address (enum machine_mode mode, rtx x, + bool strict_p, struct m68k_address *address) { - if (mode != VOIDmode && GET_MODE(x) != mode) - return 0; - switch (GET_CODE(x)) + unsigned int reach; + + memset (address, 0, sizeof (*address)); + + if (mode == BLKmode) + reach = 1; + else + reach = GET_MODE_SIZE (mode); + + /* Check for (An) (mode 2). */ + if (m68k_legitimate_base_reg_p (x, strict_p)) + { + address->base = x; + return true; + } + + /* Check for -(An) and (An)+ (modes 3 and 4). */ + if ((GET_CODE (x) == PRE_DEC || GET_CODE (x) == POST_INC) + && m68k_legitimate_base_reg_p (XEXP (x, 0), strict_p)) + { + address->code = GET_CODE (x); + address->base = XEXP (x, 0); + return true; + } + + /* Check for (d16,An) (mode 5). */ + if (GET_CODE (x) == PLUS + && GET_CODE (XEXP (x, 1)) == CONST_INT + && IN_RANGE (INTVAL (XEXP (x, 1)), -0x8000, 0x8000 - reach) + && m68k_legitimate_base_reg_p (XEXP (x, 0), strict_p)) + { + address->base = XEXP (x, 0); + address->offset = XEXP (x, 1); + return true; + } + + /* Check for GOT loads. These are (bd,An,Xn) addresses if + TARGET_68020 && flag_pic == 2, otherwise they are (d16,An) + addresses. */ + if (flag_pic + && GET_CODE (x) == PLUS + && XEXP (x, 0) == pic_offset_table_rtx + && (GET_CODE (XEXP (x, 1)) == SYMBOL_REF + || GET_CODE (XEXP (x, 1)) == LABEL_REF)) + { + address->base = XEXP (x, 0); + address->offset = XEXP (x, 1); + return true; + } + + /* The ColdFire FPU only accepts addressing modes 2-5. */ + if (TARGET_COLDFIRE_FPU && GET_MODE_CLASS (mode) == MODE_FLOAT) + return false; + + /* Check for (xxx).w and (xxx).l. Also, in the TARGET_PCREL case, + check for (d16,PC) or (bd,PC,Xn) with a suppressed index register. + All these modes are variations of mode 7. */ + if (m68k_legitimate_constant_address_p (x, reach, strict_p)) + { + address->offset = x; + return true; + } + + /* Check for (d8,PC,Xn), a mode 7 form. This case is needed for + tablejumps. + + ??? do_tablejump creates these addresses before placing the target + label, so we have to assume that unplaced labels are jump table + references. It seems unlikely that we would ever generate indexed + accesses to unplaced labels in other cases. */ + if (GET_CODE (x) == PLUS + && m68k_jump_table_ref_p (XEXP (x, 1)) + && m68k_decompose_index (XEXP (x, 0), strict_p, address)) + { + address->offset = XEXP (x, 1); + return true; + } + + /* Everything hereafter deals with (d8,An,Xn.SIZE*SCALE) or + (bd,An,Xn.SIZE*SCALE) addresses. */ + + if (TARGET_68020) + { + /* Check for a nonzero base displacement. */ + if (GET_CODE (x) == PLUS + && m68k_legitimate_constant_address_p (XEXP (x, 1), reach, strict_p)) + { + address->offset = XEXP (x, 1); + x = XEXP (x, 0); + } + + /* Check for a suppressed index register. */ + if (m68k_legitimate_base_reg_p (x, strict_p)) + { + address->base = x; + return true; + } + + /* Check for a suppressed base register. Do not allow this case + for non-symbolic offsets as it effectively gives gcc freedom + to treat data registers as base registers, which can generate + worse code. */ + if (address->offset + && symbolic_operand (address->offset, VOIDmode) + && m68k_decompose_index (x, strict_p, address)) + return true; + } + else + { + /* Check for a nonzero base displacement. */ + if (GET_CODE (x) == PLUS + && GET_CODE (XEXP (x, 1)) == CONST_INT + && IN_RANGE (INTVAL (XEXP (x, 1)), -0x80, 0x80 - reach)) + { + address->offset = XEXP (x, 1); + x = XEXP (x, 0); + } + } + + /* We now expect the sum of a base and an index. */ + if (GET_CODE (x) == PLUS) + { + if (m68k_legitimate_base_reg_p (XEXP (x, 0), strict_p) + && m68k_decompose_index (XEXP (x, 1), strict_p, address)) + { + address->base = XEXP (x, 0); + return true; + } + + if (m68k_legitimate_base_reg_p (XEXP (x, 1), strict_p) + && m68k_decompose_index (XEXP (x, 0), strict_p, address)) { - case SIGN_EXTEND : - case ZERO_EXTEND : - return 1; - default : - return 0; + address->base = XEXP (x, 1); + return true; } + } + return false; +} + +/* Return true if X is a legitimate address for values of mode MODE. + STRICT_P says whether strict checking is needed. */ + +bool +m68k_legitimate_address_p (enum machine_mode mode, rtx x, bool strict_p) +{ + struct m68k_address address; + + return m68k_decompose_address (mode, x, strict_p, &address); +} + +/* Return true if X is a memory, describing its address in ADDRESS if so. + Apply strict checking if called during or after reload. */ + +static bool +m68k_legitimate_mem_p (rtx x, struct m68k_address *address) +{ + return (MEM_P (x) + && m68k_decompose_address (GET_MODE (x), XEXP (x, 0), + reload_in_progress || reload_completed, + address)); +} + +/* Return true if X matches the 'Q' constraint. It must be a memory + with a base address and no constant offset or index. */ + +bool +m68k_matches_q_p (rtx x) +{ + struct m68k_address address; + + return (m68k_legitimate_mem_p (x, &address) + && address.code == UNKNOWN + && address.base + && !address.offset + && !address.index); +} + +/* Return true if X matches the 'U' constraint. It must be a base address + with a constant offset and no index. */ + +bool +m68k_matches_u_p (rtx x) +{ + struct m68k_address address; + + return (m68k_legitimate_mem_p (x, &address) + && address.code == UNKNOWN + && address.base + && address.offset + && !address.index); } - /* Legitimize PIC addresses. If the address is already position-independent, we return ORIG. Newly generated position-independent addresses go to REG. If we need more @@ -1990,23 +2066,42 @@ extend_operator(x, mode) handled. */ rtx -legitimize_pic_address (orig, mode, reg) - rtx orig, reg; - enum machine_mode mode ATTRIBUTE_UNUSED; +legitimize_pic_address (rtx orig, enum machine_mode mode ATTRIBUTE_UNUSED, + rtx reg) { rtx pic_ref = orig; /* First handle a simple SYMBOL_REF or LABEL_REF */ if (GET_CODE (orig) == SYMBOL_REF || GET_CODE (orig) == LABEL_REF) { - if (reg == 0) - abort (); + gcc_assert (reg); + + if (TARGET_COLDFIRE && TARGET_XGOT) + /* When compiling with -mxgot switch the code for the above + example will look like this: - pic_ref = gen_rtx_MEM (Pmode, - gen_rtx_PLUS (Pmode, - pic_offset_table_rtx, orig)); - current_function_uses_pic_offset_table = 1; - RTX_UNCHANGING_P (pic_ref) = 1; + movel a5, a0 + addl _foo@GOT, a0 + movel a0@, a0 + movel #12345, a0@ */ + { + rtx pic_offset; + + /* Wrap ORIG in UNSPEC_GOTOFF to tip m68k_output_addr_const_extra + to put @GOT after reference. */ + pic_offset = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, orig), + UNSPEC_GOTOFF); + pic_offset = gen_rtx_CONST (Pmode, pic_offset); + emit_move_insn (reg, pic_offset); + emit_insn (gen_addsi3 (reg, reg, pic_offset_table_rtx)); + pic_ref = gen_rtx_MEM (Pmode, reg); + } + else + pic_ref = gen_rtx_MEM (Pmode, + gen_rtx_PLUS (Pmode, + pic_offset_table_rtx, orig)); + crtl->uses_pic_offset_table = 1; + MEM_READONLY_P (pic_ref) = 1; emit_move_insn (reg, pic_ref); return reg; } @@ -2014,22 +2109,19 @@ legitimize_pic_address (orig, mode, reg) { rtx base; - /* Make sure this is CONST has not already been legitimized */ + /* Make sure this has not already been legitimized. */ if (GET_CODE (XEXP (orig, 0)) == PLUS && XEXP (XEXP (orig, 0), 0) == pic_offset_table_rtx) return orig; - if (reg == 0) - abort (); + gcc_assert (reg); /* legitimize both operands of the PLUS */ - if (GET_CODE (XEXP (orig, 0)) == PLUS) - { - base = legitimize_pic_address (XEXP (XEXP (orig, 0), 0), Pmode, reg); - orig = legitimize_pic_address (XEXP (XEXP (orig, 0), 1), Pmode, - base == reg ? 0 : reg); - } - else abort (); + gcc_assert (GET_CODE (XEXP (orig, 0)) == PLUS); + + base = legitimize_pic_address (XEXP (XEXP (orig, 0), 0), Pmode, reg); + orig = legitimize_pic_address (XEXP (XEXP (orig, 0), 1), Pmode, + base == reg ? 0 : reg); if (GET_CODE (orig) == CONST_INT) return plus_constant (base, INTVAL (orig)); @@ -2040,26 +2132,22 @@ legitimize_pic_address (orig, mode, reg) } -typedef enum { MOVL, SWAP, NEGW, NOTW, NOTB, MOVQ } CONST_METHOD; -static CONST_METHOD const_method PARAMS ((rtx)); +#define USE_MOVQ(i) ((unsigned) ((i) + 128) <= 255) -#define USE_MOVQ(i) ((unsigned)((i) + 128) <= 255) +/* Return the type of move that should be used for integer I. */ -static CONST_METHOD -const_method (constant) - rtx constant; +M68K_CONST_METHOD +m68k_const_method (HOST_WIDE_INT i) { - int i; unsigned u; - i = INTVAL (constant); if (USE_MOVQ (i)) return MOVQ; - /* The Coldfire doesn't have byte or word operations. */ - /* FIXME: This may not be useful for the m68060 either */ - if (!TARGET_5200) + /* The ColdFire doesn't have byte or word operations. */ + /* FIXME: This may not be useful for the m68060 either. */ + if (!TARGET_COLDFIRE) { /* if -256 < N < 256 but N is not in range for a moveq N^ff will be, so use moveq #N^ff, dreg; not.b dreg. */ @@ -2071,124 +2159,285 @@ const_method (constant) /* This is the only value where neg.w is useful */ if (i == -65408) return NEGW; - /* Try also with swap */ - u = i; - if (USE_MOVQ ((u >> 16) | (u << 16))) - return SWAP; } + + /* Try also with swap. */ + u = i; + if (USE_MOVQ ((u >> 16) | (u << 16))) + return SWAP; + + if (TARGET_ISAB) + { + /* Try using MVZ/MVS with an immediate value to load constants. */ + if (i >= 0 && i <= 65535) + return MVZ; + if (i >= -32768 && i <= 32767) + return MVS; + } + /* Otherwise, use move.l */ return MOVL; } -int -const_int_cost (constant) - rtx constant; +/* Return the cost of moving constant I into a data register. */ + +static int +const_int_cost (HOST_WIDE_INT i) { - switch (const_method (constant)) - { - case MOVQ : - /* Constants between -128 and 127 are cheap due to moveq */ - return 0; - case NOTB : - case NOTW : - case NEGW : - case SWAP : - /* Constants easily generated by moveq + not.b/not.w/neg.w/swap */ - return 1; - case MOVL : - return 2; - default : - abort (); + switch (m68k_const_method (i)) + { + case MOVQ: + /* Constants between -128 and 127 are cheap due to moveq. */ + return 0; + case MVZ: + case MVS: + case NOTB: + case NOTW: + case NEGW: + case SWAP: + /* Constants easily generated by moveq + not.b/not.w/neg.w/swap. */ + return 1; + case MOVL: + return 2; + default: + gcc_unreachable (); } } -const char * -output_move_const_into_data_reg (operands) - rtx *operands; +static bool +m68k_rtx_costs (rtx x, int code, int outer_code, int *total, + bool speed ATTRIBUTE_UNUSED) { - int i; + switch (code) + { + case CONST_INT: + /* Constant zero is super cheap due to clr instruction. */ + if (x == const0_rtx) + *total = 0; + else + *total = const_int_cost (INTVAL (x)); + return true; + + case CONST: + case LABEL_REF: + case SYMBOL_REF: + *total = 3; + return true; + + case CONST_DOUBLE: + /* Make 0.0 cheaper than other floating constants to + encourage creating tstsf and tstdf insns. */ + if (outer_code == COMPARE + && (x == CONST0_RTX (SFmode) || x == CONST0_RTX (DFmode))) + *total = 4; + else + *total = 5; + return true; + + /* These are vaguely right for a 68020. */ + /* The costs for long multiply have been adjusted to work properly + in synth_mult on the 68020, relative to an average of the time + for add and the time for shift, taking away a little more because + sometimes move insns are needed. */ + /* div?.w is relatively cheaper on 68000 counted in COSTS_N_INSNS + terms. */ +#define MULL_COST \ + (TUNE_68060 ? 2 \ + : TUNE_68040 ? 5 \ + : (TUNE_CFV2 && TUNE_EMAC) ? 3 \ + : (TUNE_CFV2 && TUNE_MAC) ? 4 \ + : TUNE_CFV2 ? 8 \ + : TARGET_COLDFIRE ? 3 : 13) + +#define MULW_COST \ + (TUNE_68060 ? 2 \ + : TUNE_68040 ? 3 \ + : TUNE_68000_10 ? 5 \ + : (TUNE_CFV2 && TUNE_EMAC) ? 3 \ + : (TUNE_CFV2 && TUNE_MAC) ? 2 \ + : TUNE_CFV2 ? 8 \ + : TARGET_COLDFIRE ? 2 : 8) + +#define DIVW_COST \ + (TARGET_CF_HWDIV ? 11 \ + : TUNE_68000_10 || TARGET_COLDFIRE ? 12 : 27) + + case PLUS: + /* An lea costs about three times as much as a simple add. */ + if (GET_MODE (x) == SImode + && GET_CODE (XEXP (x, 1)) == REG + && GET_CODE (XEXP (x, 0)) == MULT + && GET_CODE (XEXP (XEXP (x, 0), 0)) == REG + && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT + && (INTVAL (XEXP (XEXP (x, 0), 1)) == 2 + || INTVAL (XEXP (XEXP (x, 0), 1)) == 4 + || INTVAL (XEXP (XEXP (x, 0), 1)) == 8)) + { + /* lea an@(dx:l:i),am */ + *total = COSTS_N_INSNS (TARGET_COLDFIRE ? 2 : 3); + return true; + } + return false; + + case ASHIFT: + case ASHIFTRT: + case LSHIFTRT: + if (TUNE_68060) + { + *total = COSTS_N_INSNS(1); + return true; + } + if (TUNE_68000_10) + { + if (GET_CODE (XEXP (x, 1)) == CONST_INT) + { + if (INTVAL (XEXP (x, 1)) < 16) + *total = COSTS_N_INSNS (2) + INTVAL (XEXP (x, 1)) / 2; + else + /* We're using clrw + swap for these cases. */ + *total = COSTS_N_INSNS (4) + (INTVAL (XEXP (x, 1)) - 16) / 2; + } + else + *total = COSTS_N_INSNS (10); /* Worst case. */ + return true; + } + /* A shift by a big integer takes an extra instruction. */ + if (GET_CODE (XEXP (x, 1)) == CONST_INT + && (INTVAL (XEXP (x, 1)) == 16)) + { + *total = COSTS_N_INSNS (2); /* clrw;swap */ + return true; + } + if (GET_CODE (XEXP (x, 1)) == CONST_INT + && !(INTVAL (XEXP (x, 1)) > 0 + && INTVAL (XEXP (x, 1)) <= 8)) + { + *total = COSTS_N_INSNS (TARGET_COLDFIRE ? 1 : 3); /* lsr #i,dn */ + return true; + } + return false; + + case MULT: + if ((GET_CODE (XEXP (x, 0)) == ZERO_EXTEND + || GET_CODE (XEXP (x, 0)) == SIGN_EXTEND) + && GET_MODE (x) == SImode) + *total = COSTS_N_INSNS (MULW_COST); + else if (GET_MODE (x) == QImode || GET_MODE (x) == HImode) + *total = COSTS_N_INSNS (MULW_COST); + else + *total = COSTS_N_INSNS (MULL_COST); + return true; + + case DIV: + case UDIV: + case MOD: + case UMOD: + if (GET_MODE (x) == QImode || GET_MODE (x) == HImode) + *total = COSTS_N_INSNS (DIVW_COST); /* div.w */ + else if (TARGET_CF_HWDIV) + *total = COSTS_N_INSNS (18); + else + *total = COSTS_N_INSNS (43); /* div.l */ + return true; + + default: + return false; + } +} + +/* Return an instruction to move CONST_INT OPERANDS[1] into data register + OPERANDS[0]. */ + +static const char * +output_move_const_into_data_reg (rtx *operands) +{ + HOST_WIDE_INT i; i = INTVAL (operands[1]); - switch (const_method (operands[1])) + switch (m68k_const_method (i)) { - case MOVQ : -#if defined (MOTOROLA) && !defined (CRDS) - return "moveq%.l %1,%0"; -#else + case MVZ: + return "mvzw %1,%0"; + case MVS: + return "mvsw %1,%0"; + case MOVQ: return "moveq %1,%0"; -#endif - case NOTB : + case NOTB: + CC_STATUS_INIT; operands[1] = GEN_INT (i ^ 0xff); -#if defined (MOTOROLA) && !defined (CRDS) - return "moveq%.l %1,%0\n\tnot%.b %0"; -#else return "moveq %1,%0\n\tnot%.b %0"; -#endif - case NOTW : + case NOTW: + CC_STATUS_INIT; operands[1] = GEN_INT (i ^ 0xffff); -#if defined (MOTOROLA) && !defined (CRDS) - return "moveq%.l %1,%0\n\tnot%.w %0"; -#else return "moveq %1,%0\n\tnot%.w %0"; -#endif - case NEGW : -#if defined (MOTOROLA) && !defined (CRDS) - return "moveq%.l %#-128,%0\n\tneg%.w %0"; -#else - return "moveq %#-128,%0\n\tneg%.w %0"; -#endif - case SWAP : + case NEGW: + CC_STATUS_INIT; + return "moveq #-128,%0\n\tneg%.w %0"; + case SWAP: { unsigned u = i; operands[1] = GEN_INT ((u << 16) | (u >> 16)); -#if defined (MOTOROLA) && !defined (CRDS) - return "moveq%.l %1,%0\n\tswap %0"; -#else return "moveq %1,%0\n\tswap %0"; -#endif } - case MOVL : - return "move%.l %1,%0"; - default : - abort (); + case MOVL: + return "move%.l %1,%0"; + default: + gcc_unreachable (); } } -const char * -output_move_simode_const (operands) - rtx *operands; +/* Return true if I can be handled by ISA B's mov3q instruction. */ + +bool +valid_mov3q_const (HOST_WIDE_INT i) { - if (operands[1] == const0_rtx - && (DATA_REG_P (operands[0]) - || GET_CODE (operands[0]) == MEM) - /* clr insns on 68000 read before writing. - This isn't so on the 68010, but we have no TARGET_68010. */ - && ((TARGET_68020 || TARGET_5200) - || !(GET_CODE (operands[0]) == MEM - && MEM_VOLATILE_P (operands[0])))) + return TARGET_ISAB && (i == -1 || IN_RANGE (i, 1, 7)); +} + +/* Return an instruction to move CONST_INT OPERANDS[1] into OPERANDS[0]. + I is the value of OPERANDS[1]. */ + +static const char * +output_move_simode_const (rtx *operands) +{ + rtx dest; + HOST_WIDE_INT src; + + dest = operands[0]; + src = INTVAL (operands[1]); + if (src == 0 + && (DATA_REG_P (dest) || MEM_P (dest)) + /* clr insns on 68000 read before writing. */ + && ((TARGET_68010 || TARGET_COLDFIRE) + || !(MEM_P (dest) && MEM_VOLATILE_P (dest)))) return "clr%.l %0"; - else if (operands[1] == const0_rtx - && ADDRESS_REG_P (operands[0])) + else if (GET_MODE (dest) == SImode && valid_mov3q_const (src)) + return "mov3q%.l %1,%0"; + else if (src == 0 && ADDRESS_REG_P (dest)) return "sub%.l %0,%0"; - else if (DATA_REG_P (operands[0])) + else if (DATA_REG_P (dest)) return output_move_const_into_data_reg (operands); - else if (ADDRESS_REG_P (operands[0]) - && INTVAL (operands[1]) < 0x8000 - && INTVAL (operands[1]) >= -0x8000) - return "move%.w %1,%0"; - else if (GET_CODE (operands[0]) == MEM - && GET_CODE (XEXP (operands[0], 0)) == PRE_DEC - && REGNO (XEXP (XEXP (operands[0], 0), 0)) == STACK_POINTER_REGNUM - && INTVAL (operands[1]) < 0x8000 - && INTVAL (operands[1]) >= -0x8000) - return "pea %a1"; + else if (ADDRESS_REG_P (dest) && IN_RANGE (src, -0x8000, 0x7fff)) + { + if (valid_mov3q_const (src)) + return "mov3q%.l %1,%0"; + return "move%.w %1,%0"; + } + else if (MEM_P (dest) + && GET_CODE (XEXP (dest, 0)) == PRE_DEC + && REGNO (XEXP (XEXP (dest, 0), 0)) == STACK_POINTER_REGNUM + && IN_RANGE (src, -0x8000, 0x7fff)) + { + if (valid_mov3q_const (src)) + return "mov3q%.l %1,%-"; + return "pea %a1"; + } return "move%.l %1,%0"; } const char * -output_move_simode (operands) - rtx *operands; +output_move_simode (rtx *operands) { if (GET_CODE (operands[1]) == CONST_INT) return output_move_simode_const (operands); @@ -2204,17 +2453,15 @@ output_move_simode (operands) } const char * -output_move_himode (operands) - rtx *operands; +output_move_himode (rtx *operands) { if (GET_CODE (operands[1]) == CONST_INT) { if (operands[1] == const0_rtx && (DATA_REG_P (operands[0]) || GET_CODE (operands[0]) == MEM) - /* clr insns on 68000 read before writing. - This isn't so on the 68010, but we have no TARGET_68010. */ - && ((TARGET_68020 || TARGET_5200) + /* clr insns on 68000 read before writing. */ + && ((TARGET_68010 || TARGET_COLDFIRE) || !(GET_CODE (operands[0]) == MEM && MEM_VOLATILE_P (operands[0])))) return "clr%.w %0"; @@ -2224,99 +2471,37 @@ output_move_himode (operands) else if (DATA_REG_P (operands[0]) && INTVAL (operands[1]) < 128 && INTVAL (operands[1]) >= -128) - { -#if defined(MOTOROLA) && !defined(CRDS) - return "moveq%.l %1,%0"; -#else - return "moveq %1,%0"; -#endif - } + return "moveq %1,%0"; else if (INTVAL (operands[1]) < 0x8000 && INTVAL (operands[1]) >= -0x8000) return "move%.w %1,%0"; } else if (CONSTANT_P (operands[1])) return "move%.l %1,%0"; -#ifndef SGS_NO_LI - /* Recognize the insn before a tablejump, one that refers - to a table of offsets. Such an insn will need to refer - to a label on the insn. So output one. Use the label-number - of the table of offsets to generate this label. This code, - and similar code below, assumes that there will be at most one - reference to each table. */ - if (GET_CODE (operands[1]) == MEM - && GET_CODE (XEXP (operands[1], 0)) == PLUS - && GET_CODE (XEXP (XEXP (operands[1], 0), 1)) == LABEL_REF - && GET_CODE (XEXP (XEXP (operands[1], 0), 0)) != PLUS) - { - rtx labelref = XEXP (XEXP (operands[1], 0), 1); -#if defined (MOTOROLA) && !defined (SGS_SWITCH_TABLES) -#ifdef SGS - asm_fprintf (asm_out_file, "\tset %LLI%d,.+2\n", - CODE_LABEL_NUMBER (XEXP (labelref, 0))); -#else /* not SGS */ - asm_fprintf (asm_out_file, "\t.set %LLI%d,.+2\n", - CODE_LABEL_NUMBER (XEXP (labelref, 0))); -#endif /* not SGS */ -#else /* SGS_SWITCH_TABLES or not MOTOROLA */ - ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "LI", - CODE_LABEL_NUMBER (XEXP (labelref, 0))); -#ifdef SGS_SWITCH_TABLES - /* Set flag saying we need to define the symbol - LD%n (with value L%n-LI%n) at the end of the switch table. */ - switch_table_difference_label_flag = 1; -#endif /* SGS_SWITCH_TABLES */ -#endif /* SGS_SWITCH_TABLES or not MOTOROLA */ - } -#endif /* SGS_NO_LI */ return "move%.w %1,%0"; } const char * -output_move_qimode (operands) - rtx *operands; +output_move_qimode (rtx *operands) { - rtx xoperands[4]; - - /* This is probably useless, since it loses for pushing a struct - of several bytes a byte at a time. */ /* 68k family always modifies the stack pointer by at least 2, even for - byte pushes. The 5200 (coldfire) does not do this. */ - if (GET_CODE (operands[0]) == MEM - && GET_CODE (XEXP (operands[0], 0)) == PRE_DEC - && XEXP (XEXP (operands[0], 0), 0) == stack_pointer_rtx - && ! ADDRESS_REG_P (operands[1]) - && ! TARGET_5200) - { - xoperands[1] = operands[1]; - xoperands[2] - = gen_rtx_MEM (QImode, - gen_rtx_PLUS (VOIDmode, stack_pointer_rtx, const1_rtx)); - /* Just pushing a byte puts it in the high byte of the halfword. */ - /* We must put it in the low-order, high-numbered byte. */ - if (!reg_mentioned_p (stack_pointer_rtx, operands[1])) - { - xoperands[3] = stack_pointer_rtx; -#ifndef NO_ADDSUB_Q - output_asm_insn ("subq%.l %#2,%3\n\tmove%.b %1,%2", xoperands); -#else - output_asm_insn ("sub%.l %#2,%3\n\tmove%.b %1,%2", xoperands); -#endif - } - else - output_asm_insn ("move%.b %1,%-\n\tmove%.b %@,%2", xoperands); - return ""; - } - - /* clr and st insns on 68000 read before writing. - This isn't so on the 68010, but we have no TARGET_68010. */ + byte pushes. The 5200 (ColdFire) does not do this. */ + + /* This case is generated by pushqi1 pattern now. */ + gcc_assert (!(GET_CODE (operands[0]) == MEM + && GET_CODE (XEXP (operands[0], 0)) == PRE_DEC + && XEXP (XEXP (operands[0], 0), 0) == stack_pointer_rtx + && ! ADDRESS_REG_P (operands[1]) + && ! TARGET_COLDFIRE)); + + /* clr and st insns on 68000 read before writing. */ if (!ADDRESS_REG_P (operands[0]) - && ((TARGET_68020 || TARGET_5200) + && ((TARGET_68010 || TARGET_COLDFIRE) || !(GET_CODE (operands[0]) == MEM && MEM_VOLATILE_P (operands[0])))) { if (operands[1] == const0_rtx) return "clr%.b %0"; - if ((!TARGET_5200 || DATA_REG_P (operands[0])) + if ((!TARGET_COLDFIRE || DATA_REG_P (operands[0])) && GET_CODE (operands[1]) == CONST_INT && (INTVAL (operands[1]) & 255) == 255) { @@ -2328,18 +2513,12 @@ output_move_qimode (operands) && DATA_REG_P (operands[0]) && INTVAL (operands[1]) < 128 && INTVAL (operands[1]) >= -128) - { -#if defined(MOTOROLA) && !defined(CRDS) - return "moveq%.l %1,%0"; -#else - return "moveq %1,%0"; -#endif - } + return "moveq %1,%0"; if (operands[1] == const0_rtx && ADDRESS_REG_P (operands[0])) return "sub%.l %0,%0"; if (GET_CODE (operands[1]) != CONST_INT && CONSTANT_P (operands[1])) return "move%.l %1,%0"; - /* 68k family (including the 5200 coldfire) does not support byte moves to + /* 68k family (including the 5200 ColdFire) does not support byte moves to from address registers. */ if (ADDRESS_REG_P (operands[0]) || ADDRESS_REG_P (operands[1])) return "move%.w %1,%0"; @@ -2347,26 +2526,22 @@ output_move_qimode (operands) } const char * -output_move_stricthi (operands) - rtx *operands; +output_move_stricthi (rtx *operands) { if (operands[1] == const0_rtx - /* clr insns on 68000 read before writing. - This isn't so on the 68010, but we have no TARGET_68010. */ - && ((TARGET_68020 || TARGET_5200) + /* clr insns on 68000 read before writing. */ + && ((TARGET_68010 || TARGET_COLDFIRE) || !(GET_CODE (operands[0]) == MEM && MEM_VOLATILE_P (operands[0])))) return "clr%.w %0"; return "move%.w %1,%0"; } const char * -output_move_strictqi (operands) - rtx *operands; +output_move_strictqi (rtx *operands) { if (operands[1] == const0_rtx - /* clr insns on 68000 read before writing. - This isn't so on the 68010, but we have no TARGET_68010. */ - && ((TARGET_68020 || TARGET_5200) + /* clr insns on 68000 read before writing. */ + && ((TARGET_68010 || TARGET_COLDFIRE) || !(GET_CODE (operands[0]) == MEM && MEM_VOLATILE_P (operands[0])))) return "clr%.b %0"; return "move%.b %1,%0"; @@ -2376,25 +2551,26 @@ output_move_strictqi (operands) for moving operands[1] into operands[0] as a fullword. */ static const char * -singlemove_string (operands) - rtx *operands; +singlemove_string (rtx *operands) { -#ifdef SUPPORT_SUN_FPA - if (FPA_REG_P (operands[0]) || FPA_REG_P (operands[1])) - return "fpmoves %1,%0"; -#endif if (GET_CODE (operands[1]) == CONST_INT) return output_move_simode_const (operands); return "move%.l %1,%0"; } -/* Output assembler code to perform a doubleword move insn - with operands OPERANDS. */ +/* Output assembler or rtl code to perform a doubleword move insn + with operands OPERANDS. + Pointers to 3 helper functions should be specified: + HANDLE_REG_ADJUST to adjust a register by a small value, + HANDLE_COMPADR to compute an address and + HANDLE_MOVSI to move 4 bytes. */ -const char * -output_move_double (operands) - rtx *operands; +static void +handle_move_double (rtx operands[2], + void (*handle_reg_adjust) (rtx, int), + void (*handle_compadr) (rtx [2]), + void (*handle_movsi) (rtx [2])) { enum { @@ -2440,12 +2616,10 @@ output_move_double (operands) else optype1 = RNDOP; - /* Check for the cases that the operand constraints are not - supposed to allow to happen. Abort if we get one, - because generating code for these cases is painful. */ - - if (optype0 == RNDOP || optype1 == RNDOP) - abort (); + /* Check for the cases that the operand constraints are not supposed + to allow to happen. Generating code for these cases is + painful. */ + gcc_assert (optype0 != RNDOP && optype1 != RNDOP); /* If one operand is decrementing and one is incrementing decrement the former register explicitly @@ -2454,10 +2628,9 @@ output_move_double (operands) if (optype0 == PUSHOP && optype1 == POPOP) { operands[0] = XEXP (XEXP (operands[0], 0), 0); - if (size == 12) - output_asm_insn ("sub%.l %#12,%0", operands); - else - output_asm_insn ("subq%.l %#8,%0", operands); + + handle_reg_adjust (operands[0], -size); + if (GET_MODE (operands[1]) == XFmode) operands[0] = gen_rtx_MEM (XFmode, operands[0]); else if (GET_MODE (operands[0]) == DFmode) @@ -2469,10 +2642,9 @@ output_move_double (operands) if (optype0 == POPOP && optype1 == PUSHOP) { operands[1] = XEXP (XEXP (operands[1], 0), 0); - if (size == 12) - output_asm_insn ("sub%.l %#12,%1", operands); - else - output_asm_insn ("subq%.l %#8,%1", operands); + + handle_reg_adjust (operands[1], -size); + if (GET_MODE (operands[1]) == XFmode) operands[1] = gen_rtx_MEM (XFmode, operands[1]); else if (GET_MODE (operands[1]) == DFmode) @@ -2514,8 +2686,8 @@ output_move_double (operands) } else { - middlehalf[0] = operands[0]; - latehalf[0] = operands[0]; + middlehalf[0] = adjust_address (operands[0], SImode, 0); + latehalf[0] = adjust_address (operands[0], SImode, 0); } if (optype1 == REGOP) @@ -2541,21 +2713,17 @@ output_move_double (operands) middlehalf[1] = GEN_INT (l[1]); latehalf[1] = GEN_INT (l[2]); } - else if (CONSTANT_P (operands[1])) + else { - /* actually, no non-CONST_DOUBLE constant should ever - appear here. */ - abort (); - if (GET_CODE (operands[1]) == CONST_INT && INTVAL (operands[1]) < 0) - latehalf[1] = constm1_rtx; - else - latehalf[1] = const0_rtx; + /* No non-CONST_DOUBLE constant should ever appear + here. */ + gcc_assert (!CONSTANT_P (operands[1])); } } else { - middlehalf[1] = operands[1]; - latehalf[1] = operands[1]; + middlehalf[1] = adjust_address (operands[1], SImode, 0); + latehalf[1] = adjust_address (operands[1], SImode, 0); } } else @@ -2566,7 +2734,7 @@ output_move_double (operands) else if (optype0 == OFFSOP) latehalf[0] = adjust_address (operands[0], SImode, size - 4); else - latehalf[0] = operands[0]; + latehalf[0] = adjust_address (operands[0], SImode, 0); if (optype1 == REGOP) latehalf[1] = gen_rtx_REG (SImode, REGNO (operands[1]) + 1); @@ -2575,7 +2743,7 @@ output_move_double (operands) else if (optype1 == CNSTOP) split_double (operands[1], &operands[1], &latehalf[1]); else - latehalf[1] = operands[1]; + latehalf[1] = adjust_address (operands[1], SImode, 0); } /* If insn is effectively movd N(sp),-(sp) then we will do the @@ -2602,11 +2770,12 @@ output_move_double (operands) /* If both halves of dest are used in the src memory address, compute the address into latehalf of dest. Note that this can't happen if the dest is two data regs. */ -compadr: + compadr: xops[0] = latehalf[0]; xops[1] = XEXP (operands[1], 0); - output_asm_insn ("lea %a1,%0", xops); - if (GET_MODE (operands[1]) == XFmode ) + + handle_compadr (xops); + if (GET_MODE (operands[1]) == XFmode) { operands[1] = gen_rtx_MEM (XFmode, latehalf[0]); middlehalf[1] = adjust_address (operands[1], DImode, size - 8); @@ -2633,14 +2802,14 @@ compadr: goto compadr; /* JRV says this can't happen: */ - if (addreg0 || addreg1) - abort (); + gcc_assert (!addreg0 && !addreg1); /* Only the middle reg conflicts; simply put it last. */ - output_asm_insn (singlemove_string (operands), operands); - output_asm_insn (singlemove_string (latehalf), latehalf); - output_asm_insn (singlemove_string (middlehalf), middlehalf); - return ""; + handle_movsi (operands); + handle_movsi (latehalf); + handle_movsi (middlehalf); + + return; } else if (reg_overlap_mentioned_p (testlow, XEXP (operands[1], 0))) /* If the low half of dest is mentioned in the source memory @@ -2664,117 +2833,639 @@ compadr: { /* Make any unoffsettable addresses point at high-numbered word. */ if (addreg0) - { - if (size == 12) - output_asm_insn ("addq%.l %#8,%0", &addreg0); - else - output_asm_insn ("addq%.l %#4,%0", &addreg0); - } + handle_reg_adjust (addreg0, size - 4); if (addreg1) - { - if (size == 12) - output_asm_insn ("addq%.l %#8,%0", &addreg1); - else - output_asm_insn ("addq%.l %#4,%0", &addreg1); - } + handle_reg_adjust (addreg1, size - 4); /* Do that word. */ - output_asm_insn (singlemove_string (latehalf), latehalf); + handle_movsi (latehalf); /* Undo the adds we just did. */ if (addreg0) - output_asm_insn ("subq%.l %#4,%0", &addreg0); + handle_reg_adjust (addreg0, -4); if (addreg1) - output_asm_insn ("subq%.l %#4,%0", &addreg1); + handle_reg_adjust (addreg1, -4); if (size == 12) { - output_asm_insn (singlemove_string (middlehalf), middlehalf); + handle_movsi (middlehalf); + if (addreg0) - output_asm_insn ("subq%.l %#4,%0", &addreg0); + handle_reg_adjust (addreg0, -4); if (addreg1) - output_asm_insn ("subq%.l %#4,%0", &addreg1); + handle_reg_adjust (addreg1, -4); } /* Do low-numbered word. */ - return singlemove_string (operands); + + handle_movsi (operands); + return; } /* Normal case: do the two words, low-numbered first. */ - output_asm_insn (singlemove_string (operands), operands); + handle_movsi (operands); /* Do the middle one of the three words for long double */ if (size == 12) { if (addreg0) - output_asm_insn ("addq%.l %#4,%0", &addreg0); + handle_reg_adjust (addreg0, 4); if (addreg1) - output_asm_insn ("addq%.l %#4,%0", &addreg1); + handle_reg_adjust (addreg1, 4); - output_asm_insn (singlemove_string (middlehalf), middlehalf); + handle_movsi (middlehalf); } /* Make any unoffsettable addresses point at high-numbered word. */ if (addreg0) - output_asm_insn ("addq%.l %#4,%0", &addreg0); + handle_reg_adjust (addreg0, 4); if (addreg1) - output_asm_insn ("addq%.l %#4,%0", &addreg1); + handle_reg_adjust (addreg1, 4); /* Do that word. */ - output_asm_insn (singlemove_string (latehalf), latehalf); + handle_movsi (latehalf); /* Undo the adds we just did. */ if (addreg0) - { - if (size == 12) - output_asm_insn ("subq%.l %#8,%0", &addreg0); - else - output_asm_insn ("subq%.l %#4,%0", &addreg0); - } + handle_reg_adjust (addreg0, -(size - 4)); if (addreg1) - { - if (size == 12) - output_asm_insn ("subq%.l %#8,%0", &addreg1); - else - output_asm_insn ("subq%.l %#4,%0", &addreg1); - } + handle_reg_adjust (addreg1, -(size - 4)); - return ""; + return; } -/* Return a REG that occurs in ADDR with coefficient 1. - ADDR can be effectively incremented by incrementing REG. */ - -static rtx -find_addr_reg (addr) - rtx addr; +/* Output assembler code to adjust REG by N. */ +static void +output_reg_adjust (rtx reg, int n) { - while (GET_CODE (addr) == PLUS) - { - if (GET_CODE (XEXP (addr, 0)) == REG) - addr = XEXP (addr, 0); - else if (GET_CODE (XEXP (addr, 1)) == REG) - addr = XEXP (addr, 1); - else if (CONSTANT_P (XEXP (addr, 0))) - addr = XEXP (addr, 1); - else if (CONSTANT_P (XEXP (addr, 1))) - addr = XEXP (addr, 0); - else - abort (); - } - if (GET_CODE (addr) == REG) - return addr; - abort (); -} + const char *s; -/* Output assembler code to perform a 32 bit 3 operand add. */ + gcc_assert (GET_MODE (reg) == SImode + && -12 <= n && n != 0 && n <= 12); -const char * -output_addsi3 (operands) - rtx *operands; -{ - if (! operands_match_p (operands[0], operands[1])) + switch (n) + { + case 12: + s = "add%.l #12,%0"; + break; + + case 8: + s = "addq%.l #8,%0"; + break; + + case 4: + s = "addq%.l #4,%0"; + break; + + case -12: + s = "sub%.l #12,%0"; + break; + + case -8: + s = "subq%.l #8,%0"; + break; + + case -4: + s = "subq%.l #4,%0"; + break; + + default: + gcc_unreachable (); + s = NULL; + } + + output_asm_insn (s, ®); +} + +/* Emit rtl code to adjust REG by N. */ +static void +emit_reg_adjust (rtx reg1, int n) +{ + rtx reg2; + + gcc_assert (GET_MODE (reg1) == SImode + && -12 <= n && n != 0 && n <= 12); + + reg1 = copy_rtx (reg1); + reg2 = copy_rtx (reg1); + + if (n < 0) + emit_insn (gen_subsi3 (reg1, reg2, GEN_INT (-n))); + else if (n > 0) + emit_insn (gen_addsi3 (reg1, reg2, GEN_INT (n))); + else + gcc_unreachable (); +} + +/* Output assembler to load address OPERANDS[0] to register OPERANDS[1]. */ +static void +output_compadr (rtx operands[2]) +{ + output_asm_insn ("lea %a1,%0", operands); +} + +/* Output the best assembler insn for moving operands[1] into operands[0] + as a fullword. */ +static void +output_movsi (rtx operands[2]) +{ + output_asm_insn (singlemove_string (operands), operands); +} + +/* Copy OP and change its mode to MODE. */ +static rtx +copy_operand (rtx op, enum machine_mode mode) +{ + /* ??? This looks really ugly. There must be a better way + to change a mode on the operand. */ + if (GET_MODE (op) != VOIDmode) + { + if (REG_P (op)) + op = gen_rtx_REG (mode, REGNO (op)); + else + { + op = copy_rtx (op); + PUT_MODE (op, mode); + } + } + + return op; +} + +/* Emit rtl code for moving operands[1] into operands[0] as a fullword. */ +static void +emit_movsi (rtx operands[2]) +{ + operands[0] = copy_operand (operands[0], SImode); + operands[1] = copy_operand (operands[1], SImode); + + emit_insn (gen_movsi (operands[0], operands[1])); +} + +/* Output assembler code to perform a doubleword move insn + with operands OPERANDS. */ +const char * +output_move_double (rtx *operands) +{ + handle_move_double (operands, + output_reg_adjust, output_compadr, output_movsi); + + return ""; +} + +/* Output rtl code to perform a doubleword move insn + with operands OPERANDS. */ +void +m68k_emit_move_double (rtx operands[2]) +{ + handle_move_double (operands, emit_reg_adjust, emit_movsi, emit_movsi); +} + +/* Ensure mode of ORIG, a REG rtx, is MODE. Returns either ORIG or a + new rtx with the correct mode. */ + +static rtx +force_mode (enum machine_mode mode, rtx orig) +{ + if (mode == GET_MODE (orig)) + return orig; + + if (REGNO (orig) >= FIRST_PSEUDO_REGISTER) + abort (); + + return gen_rtx_REG (mode, REGNO (orig)); +} + +static int +fp_reg_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED) +{ + return reg_renumber && FP_REG_P (op); +} + +/* Emit insns to move operands[1] into operands[0]. + + Return 1 if we have written out everything that needs to be done to + do the move. Otherwise, return 0 and the caller will emit the move + normally. + + Note SCRATCH_REG may not be in the proper mode depending on how it + will be used. This routine is responsible for creating a new copy + of SCRATCH_REG in the proper mode. */ + +int +emit_move_sequence (rtx *operands, enum machine_mode mode, rtx scratch_reg) +{ + register rtx operand0 = operands[0]; + register rtx operand1 = operands[1]; + register rtx tem; + + if (scratch_reg + && reload_in_progress && GET_CODE (operand0) == REG + && REGNO (operand0) >= FIRST_PSEUDO_REGISTER) + operand0 = reg_equiv_mem[REGNO (operand0)]; + else if (scratch_reg + && reload_in_progress && GET_CODE (operand0) == SUBREG + && GET_CODE (SUBREG_REG (operand0)) == REG + && REGNO (SUBREG_REG (operand0)) >= FIRST_PSEUDO_REGISTER) + { + /* We must not alter SUBREG_BYTE (operand0) since that would confuse + the code which tracks sets/uses for delete_output_reload. */ + rtx temp = gen_rtx_SUBREG (GET_MODE (operand0), + reg_equiv_mem [REGNO (SUBREG_REG (operand0))], + SUBREG_BYTE (operand0)); + operand0 = alter_subreg (&temp); + } + + if (scratch_reg + && reload_in_progress && GET_CODE (operand1) == REG + && REGNO (operand1) >= FIRST_PSEUDO_REGISTER) + operand1 = reg_equiv_mem[REGNO (operand1)]; + else if (scratch_reg + && reload_in_progress && GET_CODE (operand1) == SUBREG + && GET_CODE (SUBREG_REG (operand1)) == REG + && REGNO (SUBREG_REG (operand1)) >= FIRST_PSEUDO_REGISTER) + { + /* We must not alter SUBREG_BYTE (operand0) since that would confuse + the code which tracks sets/uses for delete_output_reload. */ + rtx temp = gen_rtx_SUBREG (GET_MODE (operand1), + reg_equiv_mem [REGNO (SUBREG_REG (operand1))], + SUBREG_BYTE (operand1)); + operand1 = alter_subreg (&temp); + } + + if (scratch_reg && reload_in_progress && GET_CODE (operand0) == MEM + && ((tem = find_replacement (&XEXP (operand0, 0))) + != XEXP (operand0, 0))) + operand0 = gen_rtx_MEM (GET_MODE (operand0), tem); + if (scratch_reg && reload_in_progress && GET_CODE (operand1) == MEM + && ((tem = find_replacement (&XEXP (operand1, 0))) + != XEXP (operand1, 0))) + operand1 = gen_rtx_MEM (GET_MODE (operand1), tem); + + /* Handle secondary reloads for loads/stores of FP registers where + the address is symbolic by using the scratch register */ + if (fp_reg_operand (operand0, mode) + && ((GET_CODE (operand1) == MEM + && ! memory_address_p (DFmode, XEXP (operand1, 0))) + || ((GET_CODE (operand1) == SUBREG + && GET_CODE (XEXP (operand1, 0)) == MEM + && !memory_address_p (DFmode, XEXP (XEXP (operand1, 0), 0))))) + && scratch_reg) + { + if (GET_CODE (operand1) == SUBREG) + operand1 = XEXP (operand1, 0); + + /* SCRATCH_REG will hold an address. We want + it in SImode regardless of what mode it was originally given + to us. */ + scratch_reg = force_mode (SImode, scratch_reg); + + /* D might not fit in 14 bits either; for such cases load D into + scratch reg. */ + if (!memory_address_p (Pmode, XEXP (operand1, 0))) + { + emit_move_insn (scratch_reg, XEXP (XEXP (operand1, 0), 1)); + emit_move_insn (scratch_reg, gen_rtx_fmt_ee (GET_CODE (XEXP (operand1, 0)), + Pmode, + XEXP (XEXP (operand1, 0), 0), + scratch_reg)); + } + else + emit_move_insn (scratch_reg, XEXP (operand1, 0)); + emit_insn (gen_rtx_SET (VOIDmode, operand0, + gen_rtx_MEM (mode, scratch_reg))); + return 1; + } + else if (fp_reg_operand (operand1, mode) + && ((GET_CODE (operand0) == MEM + && ! memory_address_p (DFmode, XEXP (operand0, 0))) + || ((GET_CODE (operand0) == SUBREG) + && GET_CODE (XEXP (operand0, 0)) == MEM + && !memory_address_p (DFmode, XEXP (XEXP (operand0, 0), 0)))) + && scratch_reg) + { + if (GET_CODE (operand0) == SUBREG) + operand0 = XEXP (operand0, 0); + + /* SCRATCH_REG will hold an address and maybe the actual data. We want + it in SIMODE regardless of what mode it was originally given + to us. */ + scratch_reg = force_mode (SImode, scratch_reg); + + /* D might not fit in 14 bits either; for such cases load D into + scratch reg. */ + if (!memory_address_p (Pmode, XEXP (operand0, 0))) + { + emit_move_insn (scratch_reg, XEXP (XEXP (operand0, 0), 1)); + emit_move_insn (scratch_reg, gen_rtx_fmt_ee (GET_CODE (XEXP (operand0, + 0)), + Pmode, + XEXP (XEXP (operand0, 0), + 0), + scratch_reg)); + } + else + emit_move_insn (scratch_reg, XEXP (operand0, 0)); + emit_insn (gen_rtx_SET (VOIDmode, gen_rtx_MEM (mode, scratch_reg), + operand1)); + return 1; + } + /* Handle secondary reloads for loads of FP registers from constant + expressions by forcing the constant into memory. + + use scratch_reg to hold the address of the memory location. + + The proper fix is to change PREFERRED_RELOAD_CLASS to return + NO_REGS when presented with a const_int and an register class + containing only FP registers. Doing so unfortunately creates + more problems than it solves. Fix this for 2.5. */ + else if (fp_reg_operand (operand0, mode) + && CONSTANT_P (operand1) + && scratch_reg) + { + rtx xoperands[2]; + + /* SCRATCH_REG will hold an address and maybe the actual data. We want + it in SIMODE regardless of what mode it was originally given + to us. */ + scratch_reg = force_mode (SImode, scratch_reg); + + /* Force the constant into memory and put the address of the + memory location into scratch_reg. */ + xoperands[0] = scratch_reg; + xoperands[1] = XEXP (force_const_mem (mode, operand1), 0); + emit_insn (gen_rtx_SET (mode, scratch_reg, xoperands[1])); + + /* Now load the destination register. */ + emit_insn (gen_rtx_SET (mode, operand0, + gen_rtx_MEM (mode, scratch_reg))); + return 1; + } + + /* Now have insn-emit do whatever it normally does. */ + return 0; +} + +/* Split one or more DImode RTL references into pairs of SImode + references. The RTL can be REG, offsettable MEM, integer constant, or + CONST_DOUBLE. "operands" is a pointer to an array of DImode RTL to + split and "num" is its length. lo_half and hi_half are output arrays + that parallel "operands". */ + +void +split_di (rtx operands[], int num, rtx lo_half[], rtx hi_half[]) +{ + while (num--) + { + rtx op = operands[num]; + + /* simplify_subreg refuses to split volatile memory addresses, + but we still have to handle it. */ + if (GET_CODE (op) == MEM) + { + lo_half[num] = adjust_address (op, SImode, 4); + hi_half[num] = adjust_address (op, SImode, 0); + } + else + { + lo_half[num] = simplify_gen_subreg (SImode, op, + GET_MODE (op) == VOIDmode + ? DImode : GET_MODE (op), 4); + hi_half[num] = simplify_gen_subreg (SImode, op, + GET_MODE (op) == VOIDmode + ? DImode : GET_MODE (op), 0); + } + } +} + +/* 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 "fmovem %1,%a0"; + else + return "fmovem %a0,%1"; + } + else + { + if (store_p) + return "movem%.l %1,%a0"; + else + return "movem%.l %a0,%1"; + } +} + +/* Return a REG that occurs in ADDR with coefficient 1. + ADDR can be effectively incremented by incrementing REG. */ + +static rtx +find_addr_reg (rtx addr) +{ + while (GET_CODE (addr) == PLUS) + { + if (GET_CODE (XEXP (addr, 0)) == REG) + addr = XEXP (addr, 0); + else if (GET_CODE (XEXP (addr, 1)) == REG) + addr = XEXP (addr, 1); + else if (CONSTANT_P (XEXP (addr, 0))) + addr = XEXP (addr, 1); + else if (CONSTANT_P (XEXP (addr, 1))) + addr = XEXP (addr, 0); + else + gcc_unreachable (); + } + gcc_assert (GET_CODE (addr) == REG); + return addr; +} + +/* Output assembler code to perform a 32-bit 3-operand add. */ + +const char * +output_addsi3 (rtx *operands) +{ + if (! operands_match_p (operands[0], operands[1])) { if (!ADDRESS_REG_P (operands[1])) { @@ -2787,30 +3478,14 @@ output_addsi3 (operands) /* These insns can result from reloads to access stack slots over 64k from the frame pointer. */ if (GET_CODE (operands[2]) == CONST_INT - && INTVAL (operands[2]) + 0x8000 >= (unsigned) 0x10000) + && (INTVAL (operands[2]) < -32768 || INTVAL (operands[2]) > 32767)) return "move%.l %2,%0\n\tadd%.l %1,%0"; -#ifdef SGS - if (GET_CODE (operands[2]) == REG) - return "lea 0(%1,%2.l),%0"; - else - return "lea %c2(%1),%0"; -#else /* not SGS */ -#ifdef MOTOROLA - if (GET_CODE (operands[2]) == REG) - return "lea (%1,%2.l),%0"; - else - return "lea (%c2,%1),%0"; -#else /* not MOTOROLA (MIT syntax) */ if (GET_CODE (operands[2]) == REG) - return "lea %1@(0,%2:l),%0"; - else - return "lea %1@(%c2),%0"; -#endif /* not MOTOROLA */ -#endif /* not SGS */ + return MOTOROLA ? "lea (%1,%2.l),%0" : "lea %1@(0,%2:l),%0"; + return MOTOROLA ? "lea (%c2,%1),%0" : "lea %1@(%c2),%0"; } if (GET_CODE (operands[2]) == CONST_INT) { -#ifndef NO_ADDSUB_Q if (INTVAL (operands[2]) > 0 && INTVAL (operands[2]) <= 8) return "addq%.l %2,%0"; @@ -2823,34 +3498,29 @@ output_addsi3 (operands) /* On the CPU32 it is faster to use two addql instructions to add a small integer (8 < N <= 16) to a register. Likewise for subql. */ - if (TARGET_CPU32 && REG_P (operands[0])) + if (TUNE_CPU32 && REG_P (operands[0])) { if (INTVAL (operands[2]) > 8 && INTVAL (operands[2]) <= 16) { operands[2] = GEN_INT (INTVAL (operands[2]) - 8); - return "addq%.l %#8,%0\n\taddq%.l %2,%0"; + return "addq%.l #8,%0\n\taddq%.l %2,%0"; } if (INTVAL (operands[2]) < -8 && INTVAL (operands[2]) >= -16) { operands[2] = GEN_INT (- INTVAL (operands[2]) - 8); - return "subq%.l %#8,%0\n\tsubq%.l %2,%0"; + return "subq%.l #8,%0\n\tsubq%.l %2,%0"; } } -#endif if (ADDRESS_REG_P (operands[0]) && INTVAL (operands[2]) >= -0x8000 && INTVAL (operands[2]) < 0x8000) { - if (TARGET_68040) + if (TUNE_68040) return "add%.w %2,%0"; else -#ifdef MOTOROLA - return "lea (%c2,%0),%0"; -#else - return "lea %0@(%c2),%0"; -#endif + return MOTOROLA ? "lea (%c2,%0),%0" : "lea %0@(%c2),%0"; } } return "add%.l %2,%0"; @@ -2866,27 +3536,12 @@ output_addsi3 (operands) some or all of the saved cc's so they won't be used. */ void -notice_update_cc (exp, insn) - rtx exp; - rtx insn; +notice_update_cc (rtx exp, rtx insn) { - /* If the cc is being set from the fpa and the expression is not an - explicit floating point test instruction (which has code to deal with - this), reinit the CC. */ - if (((cc_status.value1 && FPA_REG_P (cc_status.value1)) - || (cc_status.value2 && FPA_REG_P (cc_status.value2))) - && !(GET_CODE (exp) == PARALLEL - && GET_CODE (XVECEXP (exp, 0, 0)) == SET - && XEXP (XVECEXP (exp, 0, 0), 0) == cc0_rtx)) - { - CC_STATUS_INIT; - } - else if (GET_CODE (exp) == SET) + if (GET_CODE (exp) == SET) { if (GET_CODE (SET_SRC (exp)) == CALL) - { - CC_STATUS_INIT; - } + CC_STATUS_INIT; else if (ADDRESS_REG_P (SET_DEST (exp))) { if (cc_status.value1 && modified_in_p (cc_status.value1, insn)) @@ -2894,15 +3549,19 @@ notice_update_cc (exp, insn) if (cc_status.value2 && modified_in_p (cc_status.value2, insn)) cc_status.value2 = 0; } + /* fmoves to memory or data registers do not set the condition + codes. Normal moves _do_ set the condition codes, but not in + a way that is appropriate for comparison with 0, because -0.0 + would be treated as a negative nonzero number. Note that it + isn't appropriate to conditionalize this restriction on + HONOR_SIGNED_ZEROS because that macro merely indicates whether + we care about the difference between -0.0 and +0.0. */ else if (!FP_REG_P (SET_DEST (exp)) && SET_DEST (exp) != cc0_rtx && (FP_REG_P (SET_SRC (exp)) || GET_CODE (SET_SRC (exp)) == FIX - || GET_CODE (SET_SRC (exp)) == FLOAT_TRUNCATE - || GET_CODE (SET_SRC (exp)) == FLOAT_EXTEND)) - { - CC_STATUS_INIT; - } + || FLOAT_MODE_P (GET_MODE (SET_DEST (exp))))) + CC_STATUS_INIT; /* A pair of move insns doesn't produce a useful overall cc. */ else if (!FP_REG_P (SET_DEST (exp)) && !FP_REG_P (SET_SRC (exp)) @@ -2910,30 +3569,27 @@ notice_update_cc (exp, insn) && (GET_CODE (SET_SRC (exp)) == REG || GET_CODE (SET_SRC (exp)) == MEM || GET_CODE (SET_SRC (exp)) == CONST_DOUBLE)) - { - CC_STATUS_INIT; - } - else if (GET_CODE (SET_SRC (exp)) == CALL) - { - CC_STATUS_INIT; - } - else if (XEXP (exp, 0) != pc_rtx) + CC_STATUS_INIT; + else if (SET_DEST (exp) != pc_rtx) { cc_status.flags = 0; - cc_status.value1 = XEXP (exp, 0); - cc_status.value2 = XEXP (exp, 1); + cc_status.value1 = SET_DEST (exp); + cc_status.value2 = SET_SRC (exp); } } else if (GET_CODE (exp) == PARALLEL && GET_CODE (XVECEXP (exp, 0, 0)) == SET) { - if (ADDRESS_REG_P (XEXP (XVECEXP (exp, 0, 0), 0))) + rtx dest = SET_DEST (XVECEXP (exp, 0, 0)); + rtx src = SET_SRC (XVECEXP (exp, 0, 0)); + + if (ADDRESS_REG_P (dest)) CC_STATUS_INIT; - else if (XEXP (XVECEXP (exp, 0, 0), 0) != pc_rtx) + else if (dest != pc_rtx) { cc_status.flags = 0; - cc_status.value1 = XEXP (XVECEXP (exp, 0, 0), 0); - cc_status.value2 = XEXP (XVECEXP (exp, 0, 0), 1); + cc_status.value1 = dest; + cc_status.value2 = src; } } else @@ -2942,16 +3598,18 @@ notice_update_cc (exp, insn) && ADDRESS_REG_P (cc_status.value2) && GET_MODE (cc_status.value2) == QImode) CC_STATUS_INIT; - if (cc_status.value2 != 0 - && !(cc_status.value1 && FPA_REG_P (cc_status.value1))) + if (cc_status.value2 != 0) switch (GET_CODE (cc_status.value2)) { - case PLUS: case MINUS: case MULT: - case DIV: case UDIV: case MOD: case UMOD: case NEG: -#if 0 /* These instructions always clear the overflow bit */ case ASHIFT: case ASHIFTRT: case LSHIFTRT: case ROTATE: case ROTATERT: -#endif + /* These instructions always clear the overflow bit, and set + the carry to the bit shifted out. */ + cc_status.flags |= CC_OVERFLOW_UNUSABLE | CC_NO_CARRY; + break; + + case PLUS: case MINUS: case MULT: + case DIV: case UDIV: case MOD: case UMOD: case NEG: if (GET_MODE (cc_status.value2) != VOIDmode) cc_status.flags |= CC_NO_OVERFLOW; break; @@ -2970,78 +3628,45 @@ notice_update_cc (exp, insn) && reg_overlap_mentioned_p (cc_status.value1, cc_status.value2)) cc_status.value2 = 0; if (((cc_status.value1 && FP_REG_P (cc_status.value1)) - || (cc_status.value2 && FP_REG_P (cc_status.value2))) - && !((cc_status.value1 && FPA_REG_P (cc_status.value1)) - || (cc_status.value2 && FPA_REG_P (cc_status.value2)))) + || (cc_status.value2 && FP_REG_P (cc_status.value2)))) cc_status.flags = CC_IN_68881; + if (cc_status.value2 && GET_CODE (cc_status.value2) == COMPARE + && GET_MODE_CLASS (GET_MODE (XEXP (cc_status.value2, 0))) == MODE_FLOAT) + { + cc_status.flags = CC_IN_68881; + if (!FP_REG_P (XEXP (cc_status.value2, 0))) + cc_status.flags |= CC_REVERSED; + } } const char * -output_move_const_double (operands) - rtx *operands; +output_move_const_double (rtx *operands) { -#ifdef SUPPORT_SUN_FPA - if (TARGET_FPA && FPA_REG_P (operands[0])) - { - int code = standard_sun_fpa_constant_p (operands[1]); - - if (code != 0) - { - static char buf[40]; + int code = standard_68881_constant_p (operands[1]); - sprintf (buf, "fpmove%%.d %%%%%d,%%0", code & 0x1ff); - return buf; - } - return "fpmove%.d %1,%0"; - } - else -#endif + if (code != 0) { - int code = standard_68881_constant_p (operands[1]); + static char buf[40]; - if (code != 0) - { - static char buf[40]; - - sprintf (buf, "fmovecr %%#0x%x,%%0", code & 0xff); - return buf; - } - return "fmove%.d %1,%0"; + sprintf (buf, "fmovecr #0x%x,%%0", code & 0xff); + return buf; } + return "fmove%.d %1,%0"; } const char * -output_move_const_single (operands) - rtx *operands; +output_move_const_single (rtx *operands) { -#ifdef SUPPORT_SUN_FPA - if (TARGET_FPA) - { - int code = standard_sun_fpa_constant_p (operands[1]); + int code = standard_68881_constant_p (operands[1]); - if (code != 0) - { - static char buf[40]; - - sprintf (buf, "fpmove%%.s %%%%%d,%%0", code & 0x1ff); - return buf; - } - return "fpmove%.s %1,%0"; - } - else -#endif /* defined SUPPORT_SUN_FPA */ + if (code != 0) { - int code = standard_68881_constant_p (operands[1]); - - if (code != 0) - { - static char buf[40]; + static char buf[40]; - sprintf (buf, "fmovecr %%#0x%x,%%0", code & 0xff); - return buf; - } - return "fmove%.s %f1,%0"; + sprintf (buf, "fmovecr #0x%x,%%0", code & 0xff); + return buf; } + return "fmove%.s %f1,%0"; } /* Return nonzero if X, a CONST_DOUBLE, has a value that we can get @@ -3061,7 +3686,7 @@ static const char *const strings_68881[7] = { "10000.0", "1e8", "1e16" - }; +}; static const int codes_68881[7] = { 0x0f, @@ -3071,7 +3696,7 @@ static const int codes_68881[7] = { 0x35, 0x36, 0x37 - }; +}; REAL_VALUE_TYPE values_68881[7]; @@ -3079,7 +3704,7 @@ REAL_VALUE_TYPE values_68881[7]; strings_68881 to binary. */ void -init_68881_table () +init_68881_table (void) { int i; REAL_VALUE_TYPE r; @@ -3097,27 +3722,15 @@ init_68881_table () } int -standard_68881_constant_p (x) - rtx x; +standard_68881_constant_p (rtx x) { REAL_VALUE_TYPE r; int i; -#ifdef NO_ASM_FMOVECR - return 0; -#endif - /* fmovecr must be emulated on the 68040 and 68060, so it shouldn't be used at all on those chips. */ - if (TARGET_68040 || TARGET_68060) - return 0; - -#ifndef REAL_ARITHMETIC -#if HOST_FLOAT_FORMAT != TARGET_FLOAT_FORMAT - if (! flag_pretend_float) + if (TUNE_68040_60) return 0; -#endif -#endif if (! inited_68881_table) init_68881_table (); @@ -3147,201 +3760,23 @@ standard_68881_constant_p (x) or 0 if X is not a power of 2. */ int -floating_exact_log2 (x) - rtx x; +floating_exact_log2 (rtx x) { REAL_VALUE_TYPE r, r1; - int i; - -#ifndef REAL_ARITHMETIC -#if HOST_FLOAT_FORMAT != TARGET_FLOAT_FORMAT - if (! flag_pretend_float) - return 0; -#endif -#endif + int exp; REAL_VALUE_FROM_CONST_DOUBLE (r, x); - if (REAL_VALUES_LESS (r, dconst0)) - return 0; - - r1 = dconst1; - i = 0; - while (REAL_VALUES_LESS (r1, r)) - { - r1 = REAL_VALUE_LDEXP (dconst1, i); - if (REAL_VALUES_EQUAL (r1, r)) - return i; - i = i + 1; - } - return 0; -} - -#ifdef SUPPORT_SUN_FPA -/* Return nonzero if X, a CONST_DOUBLE, has a value that we can get - from the Sun FPA's constant RAM. - The value returned, anded with 0x1ff, gives the code to use in fpmove - to get the desired constant. */ - -static int inited_FPA_table = 0; - -static const char *const strings_FPA[38] = { -/* small rationals */ - "0.0", - "1.0", - "0.5", - "-1.0", - "2.0", - "3.0", - "4.0", - "8.0", - "0.25", - "0.125", - "10.0", - "-0.5", -/* Decimal equivalents of double precision values */ - "2.718281828459045091", /* D_E */ - "6.283185307179586477", /* 2 pi */ - "3.141592653589793116", /* D_PI */ - "1.570796326794896619", /* pi/2 */ - "1.414213562373095145", /* D_SQRT2 */ - "0.7071067811865475244", /* 1/sqrt(2) */ - "-1.570796326794896619", /* -pi/2 */ - "1.442695040888963387", /* D_LOG2ofE */ - "3.321928024887362182", /* D_LOG2of10 */ - "0.6931471805599452862", /* D_LOGEof2 */ - "2.302585092994045901", /* D_LOGEof10 */ - "0.3010299956639811980", /* D_LOG10of2 */ - "0.4342944819032518167", /* D_LOG10ofE */ -/* Decimal equivalents of single precision values */ - "2.718281745910644531", /* S_E */ - "6.283185307179586477", /* 2 pi */ - "3.141592741012573242", /* S_PI */ - "1.570796326794896619", /* pi/2 */ - "1.414213538169860840", /* S_SQRT2 */ - "0.7071067811865475244", /* 1/sqrt(2) */ - "-1.570796326794896619", /* -pi/2 */ - "1.442695021629333496", /* S_LOG2ofE */ - "3.321928024291992188", /* S_LOG2of10 */ - "0.6931471824645996094", /* S_LOGEof2 */ - "2.302585124969482442", /* S_LOGEof10 */ - "0.3010300099849700928", /* S_LOG10of2 */ - "0.4342944920063018799", /* S_LOG10ofE */ -}; - - -static const int codes_FPA[38] = { -/* small rationals */ - 0x200, - 0xe, - 0xf, - 0x10, - 0x11, - 0xb1, - 0x12, - 0x13, - 0x15, - 0x16, - 0x17, - 0x2e, -/* double precision */ - 0x8, - 0x9, - 0xa, - 0xb, - 0xc, - 0xd, - 0x27, - 0x28, - 0x29, - 0x2a, - 0x2b, - 0x2c, - 0x2d, -/* single precision */ - 0x8, - 0x9, - 0xa, - 0xb, - 0xc, - 0xd, - 0x27, - 0x28, - 0x29, - 0x2a, - 0x2b, - 0x2c, - 0x2d - }; - -REAL_VALUE_TYPE values_FPA[38]; - -/* This code has been fixed for cross-compilation. */ - -static void init_FPA_table PARAMS ((void)); -static void -init_FPA_table () -{ - enum machine_mode mode; - int i; - REAL_VALUE_TYPE r; - - mode = DFmode; - for (i = 0; i < 38; i++) - { - if (i == 25) - mode = SFmode; - r = REAL_VALUE_ATOF (strings_FPA[i], mode); - values_FPA[i] = r; - } - inited_FPA_table = 1; -} - - -int -standard_sun_fpa_constant_p (x) - rtx x; -{ - REAL_VALUE_TYPE r; - int i; - -#ifndef REAL_ARITHMETIC -#if HOST_FLOAT_FORMAT != TARGET_FLOAT_FORMAT - if (! flag_pretend_float) + if (REAL_VALUES_LESS (r, dconst1)) return 0; -#endif -#endif - - if (! inited_FPA_table) - init_FPA_table (); - - REAL_VALUE_FROM_CONST_DOUBLE (r, x); - for (i=0; i<12; i++) - { - if (REAL_VALUES_EQUAL (r, values_FPA[i])) - return (codes_FPA[i]); - } + exp = real_exponent (&r); + real_2expN (&r1, exp, DFmode); + if (REAL_VALUES_EQUAL (r1, r)) + return exp; - if (GET_MODE (x) == SFmode) - { - for (i=25; i<38; i++) - { - if (REAL_VALUES_EQUAL (r, values_FPA[i])) - return (codes_FPA[i]); - } - } - else - { - for (i=12; i<25; i++) - { - if (REAL_VALUES_EQUAL (r, values_FPA[i])) - return (codes_FPA[i]); - } - } - return 0x0; + return 0; } -#endif /* define SUPPORT_SUN_FPA */ /* A C compound statement to output to stdio stream STREAM the assembler syntax for an instruction operand X. X is an RTL @@ -3373,121 +3808,67 @@ standard_sun_fpa_constant_p (x) '@' for a reference to the top word on the stack: sp@, (sp) or (%sp) depending on the style of syntax. '#' for an immediate operand prefix (# in MIT and Motorola syntax - but & in SGS syntax, $ in CRDS/UNOS syntax). + but & in SGS syntax). '!' for the cc register (used in an `and to cc' insn). '$' for the letter `s' in an op code, but only on the 68040. '&' for the letter `d' in an op code, but only on the 68040. '/' for register prefix needed by longlong.h. + '?' 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. 'f' for float insn (print a CONST_DOUBLE as a float rather than in hex) - 'o' for operands to go directly to output_operand_address (bypassing - print_operand_address--used only for SYMBOL_REFs under TARGET_PCREL) - 'w' for FPA insn (print a CONST_DOUBLE as a SunFPA constant rather - than directly). Second part of 'y' below. 'x' for float insn (print a CONST_DOUBLE as a float rather than in hex), or print pair of registers as rx:ry. - 'y' for a FPA insn (print pair of registers as rx:ry). This also outputs - CONST_DOUBLE's as SunFPA constant RAM registers if - possible, so it should not be used except for the SunFPA. - - */ + 'p' print an address with @PLTPC attached, but only if the operand + is not locally-bound. */ void -print_operand (file, op, letter) - FILE *file; /* file to write to */ - rtx op; /* operand to print */ - int letter; /* % or 0 */ +print_operand (FILE *file, rtx op, int letter) { -#ifdef SUPPORT_SUN_FPA - int i; -#endif - if (letter == '.') { -#if defined (MOTOROLA) && !defined (CRDS) - asm_fprintf (file, "."); -#endif + if (MOTOROLA) + fprintf (file, "."); } else if (letter == '#') - { - asm_fprintf (file, "%0I"); - } + asm_fprintf (file, "%I"); else if (letter == '-') - { -#ifdef MOTOROLA - asm_fprintf (file, "-(%Rsp)"); -#else - asm_fprintf (file, "%Rsp@-"); -#endif - } + asm_fprintf (file, MOTOROLA ? "-(%Rsp)" : "%Rsp@-"); else if (letter == '+') - { -#ifdef MOTOROLA - asm_fprintf (file, "(%Rsp)+"); -#else - asm_fprintf (file, "%Rsp@+"); -#endif - } + asm_fprintf (file, MOTOROLA ? "(%Rsp)+" : "%Rsp@+"); else if (letter == '@') - { -#ifdef MOTOROLA - asm_fprintf (file, "(%Rsp)"); -#else - asm_fprintf (file, "%Rsp@"); -#endif - } + asm_fprintf (file, MOTOROLA ? "(%Rsp)" : "%Rsp@"); else if (letter == '!') - { - asm_fprintf (file, "%Rfpcr"); - } + asm_fprintf (file, "%Rfpcr"); else if (letter == '$') { - if (TARGET_68040_ONLY) - { - fprintf (file, "s"); - } + if (TARGET_68040) + fprintf (file, "s"); } else if (letter == '&') { - if (TARGET_68040_ONLY) - { - fprintf (file, "d"); - } + if (TARGET_68040) + fprintf (file, "d"); } else if (letter == '/') + asm_fprintf (file, "%R"); + else if (letter == '?') + asm_fprintf (file, m68k_library_id_string); + else if (letter == 'p') { - asm_fprintf (file, "%R"); - } - else if (letter == 'o') - { - /* This is only for direct addresses with TARGET_PCREL */ - if (GET_CODE (op) != MEM || GET_CODE (XEXP (op, 0)) != SYMBOL_REF - || !TARGET_PCREL) - abort (); - output_addr_const (file, XEXP (op, 0)); + output_addr_const (file, op); + if (!(GET_CODE (op) == SYMBOL_REF && SYMBOL_REF_LOCAL_P (op))) + fprintf (file, "@PLTPC"); } else if (GET_CODE (op) == REG) { -#ifdef SUPPORT_SUN_FPA - if (REGNO (op) < 16 - && (letter == 'y' || letter == 'x') - && GET_MODE (op) == DFmode) - { - fprintf (file, "%s:%s", reg_names[REGNO (op)], - reg_names[REGNO (op)+1]); - } + if (letter == 'R') + /* Print out the second register name of a register pair. + I.e., R (6) => 7. */ + fputs (M68K_REGNAME(REGNO (op) + 1), file); else -#endif - { - if (letter == 'R') - /* Print out the second register name of a register pair. - I.e., R (6) => 7. */ - fputs (reg_names[REGNO (op) + 1], file); - else - fputs (reg_names[REGNO (op)], file); - } + fputs (M68K_REGNAME(REGNO (op)), file); } else if (GET_CODE (op) == MEM) { @@ -3497,45 +3878,38 @@ print_operand (file, op, letter) && !(GET_CODE (XEXP (op, 0)) == CONST_INT && INTVAL (XEXP (op, 0)) < 0x8000 && INTVAL (XEXP (op, 0)) >= -0x8000)) - { -#ifdef MOTOROLA - fprintf (file, ".l"); -#else - fprintf (file, ":l"); -#endif - } - } -#ifdef SUPPORT_SUN_FPA - else if ((letter == 'y' || letter == 'w') - && GET_CODE (op) == CONST_DOUBLE - && (i = standard_sun_fpa_constant_p (op))) - { - fprintf (file, "%%%d", i & 0x1ff); + fprintf (file, MOTOROLA ? ".l" : ":l"); } -#endif else if (GET_CODE (op) == CONST_DOUBLE && GET_MODE (op) == SFmode) { REAL_VALUE_TYPE r; + long l; REAL_VALUE_FROM_CONST_DOUBLE (r, op); - ASM_OUTPUT_FLOAT_OPERAND (letter, file, r); + REAL_VALUE_TO_TARGET_SINGLE (r, l); + asm_fprintf (file, "%I0x%lx", l & 0xFFFFFFFF); } else if (GET_CODE (op) == CONST_DOUBLE && GET_MODE (op) == XFmode) { REAL_VALUE_TYPE r; + long l[3]; REAL_VALUE_FROM_CONST_DOUBLE (r, op); - ASM_OUTPUT_LONG_DOUBLE_OPERAND (file, r); + REAL_VALUE_TO_TARGET_LONG_DOUBLE (r, l); + asm_fprintf (file, "%I0x%lx%08lx%08lx", l[0] & 0xFFFFFFFF, + l[1] & 0xFFFFFFFF, l[2] & 0xFFFFFFFF); } else if (GET_CODE (op) == CONST_DOUBLE && GET_MODE (op) == DFmode) { REAL_VALUE_TYPE r; + long l[2]; REAL_VALUE_FROM_CONST_DOUBLE (r, op); - ASM_OUTPUT_DOUBLE_OPERAND (file, r); + REAL_VALUE_TO_TARGET_DOUBLE (r, l); + asm_fprintf (file, "%I0x%lx%08lx", l[0] & 0xFFFFFFFF, l[1] & 0xFFFFFFFF); } else { /* Use `print_operand_address' instead of `output_addr_const' to ensure that we print relevant PIC stuff. */ - asm_fprintf (file, "%0I"); + asm_fprintf (file, "%I"); if (TARGET_PCREL && (GET_CODE (op) == SYMBOL_REF || GET_CODE (op) == CONST)) print_operand_address (file, op); @@ -3544,6 +3918,20 @@ print_operand (file, op, letter) } } +/* m68k implementation of OUTPUT_ADDR_CONST_EXTRA. */ + +bool +m68k_output_addr_const_extra (FILE *file, rtx x) +{ + if (GET_CODE (x) != UNSPEC || XINT (x, 1) != UNSPEC_GOTOFF) + return false; + + output_addr_const (file, XVECEXP (x, 0, 0)); + /* ??? What is the non-MOTOROLA syntax? */ + fputs ("@GOT", file); + return true; +} + /* A C compound statement to output to stdio stream STREAM the assembler syntax for an instruction operand that is a memory @@ -3557,363 +3945,175 @@ print_operand (file, op, letter) It is possible for PIC to generate a (plus (label_ref...) (reg...)) and we handle that just like we would a (plus (symbol_ref...) (reg...)). - Some SGS assemblers have a bug such that "Lnnn-LInnn-2.b(pc,d0.l*2)" - fails to assemble. Luckily "Lnnn(pc,d0.l*2)" produces the results - we want. This difference can be accommodated by using an assembler - define such "LDnnn" to be either "Lnnn-LInnn-2.b", "Lnnn", or any other - string, as necessary. This is accomplished via the ASM_OUTPUT_CASE_END - macro. See m68k/sgs.h for an example; for versions without the bug. - Some assemblers refuse all the above solutions. The workaround is to - emit "K(pc,d0.l*2)" with K being a small constant known to give the - right behaviour. - - They also do not like things like "pea 1.w", so we simple leave off - the .w on small constants. - This routine is responsible for distinguishing between -fpic and -fPIC style relocations in an address. When generating -fpic code the - offset is output in word mode (eg movel a5@(_foo:w), a0). When generating - -fPIC code the offset is output in long mode (eg movel a5@(_foo:l), a0) */ - -#ifndef ASM_OUTPUT_CASE_FETCH -#ifdef MOTOROLA -#ifdef SGS -#define ASM_OUTPUT_CASE_FETCH(file, labelno, regname)\ - asm_fprintf (file, "%LLD%d(%Rpc,%s.", labelno, regname) -#else -#define ASM_OUTPUT_CASE_FETCH(file, labelno, regname)\ - asm_fprintf (file, "%LL%d-%LLI%d.b(%Rpc,%s.", labelno, labelno, regname) -#endif -#else -#define ASM_OUTPUT_CASE_FETCH(file, labelno, regname)\ - asm_fprintf (file, "%Rpc@(%LL%d-%LLI%d-2:b,%s:", labelno, labelno, regname) -#endif -#endif /* ASM_OUTPUT_CASE_FETCH */ + offset is output in word mode (e.g. movel a5@(_foo:w), a0). When generating + -fPIC code the offset is output in long mode (e.g. movel a5@(_foo:l), a0) */ void -print_operand_address (file, addr) - FILE *file; - rtx addr; +print_operand_address (FILE *file, rtx addr) { - register rtx reg1, reg2, breg, ireg; - rtx offset; - - switch (GET_CODE (addr)) + struct m68k_address address; + + if (!m68k_decompose_address (QImode, addr, true, &address)) + gcc_unreachable (); + + if (address.code == PRE_DEC) + fprintf (file, MOTOROLA ? "-(%s)" : "%s@-", + M68K_REGNAME (REGNO (address.base))); + else if (address.code == POST_INC) + fprintf (file, MOTOROLA ? "(%s)+" : "%s@+", + M68K_REGNAME (REGNO (address.base))); + else if (!address.base && !address.index) { - case REG: -#ifdef MOTOROLA - fprintf (file, "(%s)", reg_names[REGNO (addr)]); -#else - fprintf (file, "%s@", reg_names[REGNO (addr)]); -#endif - break; - case PRE_DEC: -#ifdef MOTOROLA - fprintf (file, "-(%s)", reg_names[REGNO (XEXP (addr, 0))]); -#else - fprintf (file, "%s@-", reg_names[REGNO (XEXP (addr, 0))]); -#endif - break; - case POST_INC: -#ifdef MOTOROLA - fprintf (file, "(%s)+", reg_names[REGNO (XEXP (addr, 0))]); -#else - fprintf (file, "%s@+", reg_names[REGNO (XEXP (addr, 0))]); -#endif - break; - case PLUS: - reg1 = reg2 = ireg = breg = offset = 0; - if (CONSTANT_ADDRESS_P (XEXP (addr, 0))) - { - offset = XEXP (addr, 0); - addr = XEXP (addr, 1); - } - else if (CONSTANT_ADDRESS_P (XEXP (addr, 1))) - { - offset = XEXP (addr, 1); - addr = XEXP (addr, 0); - } - if (GET_CODE (addr) != PLUS) - { - ; - } - else if (GET_CODE (XEXP (addr, 0)) == SIGN_EXTEND) - { - reg1 = XEXP (addr, 0); - addr = XEXP (addr, 1); - } - else if (GET_CODE (XEXP (addr, 1)) == SIGN_EXTEND) - { - reg1 = XEXP (addr, 1); - addr = XEXP (addr, 0); - } - else if (GET_CODE (XEXP (addr, 0)) == MULT) - { - reg1 = XEXP (addr, 0); - addr = XEXP (addr, 1); - } - else if (GET_CODE (XEXP (addr, 1)) == MULT) - { - reg1 = XEXP (addr, 1); - addr = XEXP (addr, 0); - } - else if (GET_CODE (XEXP (addr, 0)) == REG) - { - reg1 = XEXP (addr, 0); - addr = XEXP (addr, 1); - } - else if (GET_CODE (XEXP (addr, 1)) == REG) - { - reg1 = XEXP (addr, 1); - addr = XEXP (addr, 0); - } - if (GET_CODE (addr) == REG || GET_CODE (addr) == MULT - || GET_CODE (addr) == SIGN_EXTEND) - { - if (reg1 == 0) - { - reg1 = addr; - } - else - { - reg2 = addr; - } - addr = 0; - } -#if 0 /* for OLD_INDEXING */ - else if (GET_CODE (addr) == PLUS) - { - if (GET_CODE (XEXP (addr, 0)) == REG) - { - reg2 = XEXP (addr, 0); - addr = XEXP (addr, 1); - } - else if (GET_CODE (XEXP (addr, 1)) == REG) - { - reg2 = XEXP (addr, 1); - addr = XEXP (addr, 0); - } - } -#endif - if (offset != 0) - { - if (addr != 0) - { - abort (); - } - addr = offset; - } - if ((reg1 && (GET_CODE (reg1) == SIGN_EXTEND - || GET_CODE (reg1) == MULT)) - || (reg2 != 0 && REGNO_OK_FOR_BASE_P (REGNO (reg2)))) - { - breg = reg2; - ireg = reg1; - } - else if (reg1 != 0 && REGNO_OK_FOR_BASE_P (REGNO (reg1))) - { - breg = reg1; - ireg = reg2; - } - if (ireg != 0 && breg == 0 && GET_CODE (addr) == LABEL_REF - && ! (flag_pic && ireg == pic_offset_table_rtx)) - { - int scale = 1; - if (GET_CODE (ireg) == MULT) - { - scale = INTVAL (XEXP (ireg, 1)); - ireg = XEXP (ireg, 0); - } - if (GET_CODE (ireg) == SIGN_EXTEND) - { - ASM_OUTPUT_CASE_FETCH (file, - CODE_LABEL_NUMBER (XEXP (addr, 0)), - reg_names[REGNO (XEXP (ireg, 0))]); - fprintf (file, "w"); - } - else - { - ASM_OUTPUT_CASE_FETCH (file, - CODE_LABEL_NUMBER (XEXP (addr, 0)), - reg_names[REGNO (ireg)]); - fprintf (file, "l"); - } - if (scale != 1) - { -#ifdef MOTOROLA - fprintf (file, "*%d", scale); -#else - fprintf (file, ":%d", scale); -#endif - } - putc (')', file); - break; - } - if (breg != 0 && ireg == 0 && GET_CODE (addr) == LABEL_REF - && ! (flag_pic && breg == pic_offset_table_rtx)) - { - ASM_OUTPUT_CASE_FETCH (file, - CODE_LABEL_NUMBER (XEXP (addr, 0)), - reg_names[REGNO (breg)]); - fprintf (file, "l)"); - break; - } - if (ireg != 0 || breg != 0) - { - int scale = 1; - if (breg == 0) - { - abort (); - } - if (! flag_pic && addr && GET_CODE (addr) == LABEL_REF) - { - abort (); - } -#ifdef MOTOROLA - if (addr != 0) - { - output_addr_const (file, addr); - if (flag_pic && (breg == pic_offset_table_rtx)) - { - fprintf (file, "@GOT"); - if (flag_pic == 1) - fprintf (file, ".w"); - } - } - fprintf (file, "(%s", reg_names[REGNO (breg)]); - if (ireg != 0) - { - putc (',', file); - } -#else - fprintf (file, "%s@(", reg_names[REGNO (breg)]); - if (addr != 0) - { - output_addr_const (file, addr); - if ((flag_pic == 1) && (breg == pic_offset_table_rtx)) - fprintf (file, ":w"); - if ((flag_pic == 2) && (breg == pic_offset_table_rtx)) - fprintf (file, ":l"); - } - if (addr != 0 && ireg != 0) - { - putc (',', file); - } -#endif - if (ireg != 0 && GET_CODE (ireg) == MULT) - { - scale = INTVAL (XEXP (ireg, 1)); - ireg = XEXP (ireg, 0); - } - if (ireg != 0 && GET_CODE (ireg) == SIGN_EXTEND) - { -#ifdef MOTOROLA - fprintf (file, "%s.w", reg_names[REGNO (XEXP (ireg, 0))]); -#else - fprintf (file, "%s:w", reg_names[REGNO (XEXP (ireg, 0))]); -#endif - } - else if (ireg != 0) - { -#ifdef MOTOROLA - fprintf (file, "%s.l", reg_names[REGNO (ireg)]); -#else - fprintf (file, "%s:l", reg_names[REGNO (ireg)]); -#endif - } - if (scale != 1) - { -#ifdef MOTOROLA - fprintf (file, "*%d", scale); -#else - fprintf (file, ":%d", scale); -#endif - } - putc (')', file); - break; - } - else if (reg1 != 0 && GET_CODE (addr) == LABEL_REF - && ! (flag_pic && reg1 == pic_offset_table_rtx)) - { - ASM_OUTPUT_CASE_FETCH (file, - CODE_LABEL_NUMBER (XEXP (addr, 0)), - reg_names[REGNO (reg1)]); - fprintf (file, "l)"); - break; - } - /* FALL-THROUGH (is this really what we want?) */ - default: - if (GET_CODE (addr) == CONST_INT - && INTVAL (addr) < 0x8000 - && INTVAL (addr) >= -0x8000) - { -#ifdef MOTOROLA -#ifdef SGS - /* Many SGS assemblers croak on size specifiers for constants. */ - fprintf (file, "%d", (int) INTVAL (addr)); -#else - fprintf (file, "%d.w", (int) INTVAL (addr)); -#endif -#else - fprintf (file, "%d:w", (int) INTVAL (addr)); -#endif - } - else if (GET_CODE (addr) == CONST_INT) - { + /* A constant address. */ + gcc_assert (address.offset == addr); + if (GET_CODE (addr) == CONST_INT) + { + /* (xxx).w or (xxx).l. */ + if (IN_RANGE (INTVAL (addr), -0x8000, 0x7fff)) + fprintf (file, MOTOROLA ? "%d.w" : "%d:w", (int) INTVAL (addr)); + else fprintf (file, HOST_WIDE_INT_PRINT_DEC, INTVAL (addr)); - } - else if (TARGET_PCREL) - { - fputc ('(', file); - output_addr_const (file, addr); - if (flag_pic == 1) - asm_fprintf (file, ":w,%Rpc)"); - else - asm_fprintf (file, ":l,%Rpc)"); - } - else - { - /* Special case for SYMBOL_REF if the symbol name ends in - `.', this can be mistaken as a size suffix. Put - the name in parentheses. */ - if (GET_CODE (addr) == SYMBOL_REF - && strlen (XSTR (addr, 0)) > 2 - && XSTR (addr, 0)[strlen (XSTR (addr, 0)) - 2] == '.') - { - putc ('(', file); - output_addr_const (file, addr); - putc (')', file); - } - else + } + else if (TARGET_PCREL) + { + /* (d16,PC) or (bd,PC,Xn) (with suppressed index register). */ + fputc ('(', file); + output_addr_const (file, addr); + asm_fprintf (file, flag_pic == 1 ? ":w,%Rpc)" : ":l,%Rpc)"); + } + else + { + /* (xxx).l. We need a special case for SYMBOL_REF if the symbol + name ends in `.', as the last 2 characters can be + mistaken as a size suffix. Put the name in parentheses. */ + if (GET_CODE (addr) == SYMBOL_REF + && strlen (XSTR (addr, 0)) > 2 + && XSTR (addr, 0)[strlen (XSTR (addr, 0)) - 2] == '.') + { + putc ('(', file); output_addr_const (file, addr); - } - break; + putc (')', file); + } + else + output_addr_const (file, addr); + } } -} - -/* Check for cases where a clr insns can be omitted from code using - strict_low_part sets. For example, the second clrl here is not needed: - clrl d0; movw a0@+,d0; use d0; clrl d0; movw a0@+; use d0; ... - - MODE is the mode of this STRICT_LOW_PART set. FIRST_INSN is the clear - insn we are checking for redundancy. TARGET is the register set by the - clear insn. */ - -int -strict_low_part_peephole_ok (mode, first_insn, target) - enum machine_mode mode; - rtx first_insn; - rtx target; -{ - rtx p; - - p = prev_nonnote_insn (first_insn); - - while (p) + else { + int labelno; + + /* If ADDR is a (d8,pc,Xn) address, this is the number of the + label being accessed, otherwise it is -1. */ + labelno = (address.offset + && !address.base + && GET_CODE (address.offset) == LABEL_REF + ? CODE_LABEL_NUMBER (XEXP (address.offset, 0)) + : -1); + if (MOTOROLA) + { + /* Print the "offset(base" component. */ + if (labelno >= 0) + asm_fprintf (file, "%LL%d(%Rpc,", labelno); + else + { + if (address.offset) + { + output_addr_const (file, address.offset); + if (flag_pic && address.base == pic_offset_table_rtx) + { + fprintf (file, "@GOT"); + if (flag_pic == 1 && TARGET_68020) + fprintf (file, ".w"); + } + } + putc ('(', file); + if (address.base) + fputs (M68K_REGNAME (REGNO (address.base)), file); + } + /* Print the ",index" component, if any. */ + if (address.index) + { + if (address.base) + putc (',', file); + fprintf (file, "%s.%c", + M68K_REGNAME (REGNO (address.index)), + GET_MODE (address.index) == HImode ? 'w' : 'l'); + if (address.scale != 1) + fprintf (file, "*%d", address.scale); + } + putc (')', file); + } + else /* !MOTOROLA */ + { + if (!address.offset && !address.index) + fprintf (file, "%s@", M68K_REGNAME (REGNO (address.base))); + else + { + /* Print the "base@(offset" component. */ + if (labelno >= 0) + asm_fprintf (file, "%Rpc@(%LL%d", labelno); + else + { + if (address.base) + fputs (M68K_REGNAME (REGNO (address.base)), file); + fprintf (file, "@("); + if (address.offset) + { + output_addr_const (file, address.offset); + if (address.base == pic_offset_table_rtx && TARGET_68020) + switch (flag_pic) + { + case 1: + fprintf (file, ":w"); break; + case 2: + fprintf (file, ":l"); break; + default: + break; + } + } + } + /* Print the ",index" component, if any. */ + if (address.index) + { + fprintf (file, ",%s:%c", + M68K_REGNAME (REGNO (address.index)), + GET_MODE (address.index) == HImode ? 'w' : 'l'); + if (address.scale != 1) + fprintf (file, ":%d", address.scale); + } + putc (')', file); + } + } + } +} + +/* Check for cases where a clr insns can be omitted from code using + strict_low_part sets. For example, the second clrl here is not needed: + clrl d0; movw a0@+,d0; use d0; clrl d0; movw a0@+; use d0; ... + + MODE is the mode of this STRICT_LOW_PART set. FIRST_INSN is the clear + insn we are checking for redundancy. TARGET is the register set by the + clear insn. */ + +bool +strict_low_part_peephole_ok (enum machine_mode mode, rtx first_insn, + rtx target) +{ + rtx p = first_insn; + + while ((p = PREV_INSN (p))) + { + if (NOTE_INSN_BASIC_BLOCK_P (p)) + return false; + + if (NOTE_P (p)) + continue; + /* If it isn't an insn, then give up. */ - if (GET_CODE (p) != INSN) - return 0; + if (!INSN_P (p)) + return false; if (reg_set_p (target, p)) { @@ -3922,7 +4122,7 @@ strict_low_part_peephole_ok (mode, first_insn, target) /* If it isn't an easy to recognize insn, then give up. */ if (! set) - return 0; + return false; dest = SET_DEST (set); @@ -3930,7 +4130,7 @@ strict_low_part_peephole_ok (mode, first_insn, target) first_insn is redundant. */ if (rtx_equal_p (dest, target) && SET_SRC (set) == const0_rtx) - return 1; + return true; else if (GET_CODE (dest) == STRICT_LOW_PART && GET_CODE (XEXP (dest, 0)) == REG && REGNO (XEXP (dest, 0)) == REGNO (target) @@ -3940,62 +4140,16 @@ strict_low_part_peephole_ok (mode, first_insn, target) we are using, so it is safe. */ ; else - return 0; + return false; } - - p = prev_nonnote_insn (p); - } - return 0; -} - -/* Accept integer operands in the range 0..0xffffffff. We have to check the - range carefully since this predicate is used in DImode contexts. Also, we - need some extra crud to make it work when hosted on 64-bit machines. */ - -int -const_uint32_operand (op, mode) - rtx op; - enum machine_mode mode; -{ - /* It doesn't make sense to ask this question with a mode that is - not larger than 32 bits. */ - if (GET_MODE_BITSIZE (mode) <= 32) - abort (); - -#if HOST_BITS_PER_WIDE_INT > 32 - /* All allowed constants will fit a CONST_INT. */ - return (GET_CODE (op) == CONST_INT - && (INTVAL (op) >= 0 && INTVAL (op) <= 0xffffffffL)); -#else - return (GET_CODE (op) == CONST_INT - || (GET_CODE (op) == CONST_DOUBLE && CONST_DOUBLE_HIGH (op) == 0)); -#endif -} - -/* Accept integer operands in the range -0x80000000..0x7fffffff. We have - to check the range carefully since this predicate is used in DImode - contexts. */ - -int -const_sint32_operand (op, mode) - rtx op; - enum machine_mode mode; -{ - /* It doesn't make sense to ask this question with a mode that is - not larger than 32 bits. */ - if (GET_MODE_BITSIZE (mode) <= 32) - abort (); - - /* All allowed constants will fit a CONST_INT. */ - return (GET_CODE (op) == CONST_INT - && (INTVAL (op) >= (-0x7fffffff - 1) && INTVAL (op) <= 0x7fffffff)); + return false; } /* Operand predicates for implementing asymmetric pc-relative addressing on m68k. The m68k supports pc-relative addressing (mode 7, register 2) - when used as a source operand, but not as a destintation operand. + when used as a source operand, but not as a destination operand. We model this by restricting the meaning of the basic predicates (general_operand, memory_operand, etc) to forbid the use of this @@ -4050,81 +4204,15 @@ const_sint32_operand (op, mode) ***************************************************************************/ -/* Special case of a general operand that's used as a source operand. - Use this to permit reads from PC-relative memory when -mpcrel - is specified. */ - -int -general_src_operand (op, mode) - rtx op; - enum machine_mode mode; -{ - if (TARGET_PCREL - && GET_CODE (op) == MEM - && (GET_CODE (XEXP (op, 0)) == SYMBOL_REF - || GET_CODE (XEXP (op, 0)) == LABEL_REF - || GET_CODE (XEXP (op, 0)) == CONST)) - return 1; - return general_operand (op, mode); -} - -/* Special case of a nonimmediate operand that's used as a source. - Use this to permit reads from PC-relative memory when -mpcrel - is specified. */ - -int -nonimmediate_src_operand (op, mode) - rtx op; - enum machine_mode mode; -{ - if (TARGET_PCREL && GET_CODE (op) == MEM - && (GET_CODE (XEXP (op, 0)) == SYMBOL_REF - || GET_CODE (XEXP (op, 0)) == LABEL_REF - || GET_CODE (XEXP (op, 0)) == CONST)) - return 1; - return nonimmediate_operand (op, mode); -} - -/* Special case of a memory operand that's used as a source. - Use this to permit reads from PC-relative memory when -mpcrel - is specified. */ - -int -memory_src_operand (op, mode) - rtx op; - enum machine_mode mode; -{ - if (TARGET_PCREL && GET_CODE (op) == MEM - && (GET_CODE (XEXP (op, 0)) == SYMBOL_REF - || GET_CODE (XEXP (op, 0)) == LABEL_REF - || GET_CODE (XEXP (op, 0)) == CONST)) - return 1; - return memory_operand (op, mode); -} - -/* Predicate that accepts only a pc-relative address. This is needed - because pc-relative addresses don't satisfy the predicate - "general_src_operand". */ - -int -pcrel_address (op, mode) - rtx op; - enum machine_mode mode ATTRIBUTE_UNUSED; -{ - return (GET_CODE (op) == SYMBOL_REF || GET_CODE (op) == LABEL_REF - || GET_CODE (op) == CONST); -} - const char * -output_andsi3 (operands) - rtx *operands; +output_andsi3 (rtx *operands) { int logval; if (GET_CODE (operands[2]) == CONST_INT - && (INTVAL (operands[2]) | 0xffff) == 0xffffffff + && (INTVAL (operands[2]) | 0xffff) == -1 && (DATA_REG_P (operands[0]) || offsettable_memref_p (operands[0])) - && !TARGET_5200) + && !TARGET_COLDFIRE) { if (GET_CODE (operands[0]) != REG) operands[0] = adjust_address (operands[0], HImode, 2); @@ -4141,9 +4229,7 @@ output_andsi3 (operands) || offsettable_memref_p (operands[0]))) { if (DATA_REG_P (operands[0])) - { - operands[1] = GEN_INT (logval); - } + operands[1] = GEN_INT (logval); else { operands[0] = adjust_address (operands[0], SImode, 3 - (logval / 8)); @@ -4157,15 +4243,14 @@ output_andsi3 (operands) } const char * -output_iorsi3 (operands) - rtx *operands; +output_iorsi3 (rtx *operands) { register int logval; if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) >> 16 == 0 && (DATA_REG_P (operands[0]) || offsettable_memref_p (operands[0])) - && !TARGET_5200) + && !TARGET_COLDFIRE) { if (GET_CODE (operands[0]) != REG) operands[0] = adjust_address (operands[0], HImode, 2); @@ -4194,14 +4279,13 @@ output_iorsi3 (operands) } const char * -output_xorsi3 (operands) - rtx *operands; +output_xorsi3 (rtx *operands) { register int logval; if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) >> 16 == 0 && (offsettable_memref_p (operands[0]) || DATA_REG_P (operands[0])) - && !TARGET_5200) + && !TARGET_COLDFIRE) { if (! DATA_REG_P (operands[0])) operands[0] = adjust_address (operands[0], HImode, 2); @@ -4229,12 +4313,36 @@ output_xorsi3 (operands) return "eor%.l %2,%0"; } +/* Return the instruction that should be used for a call to address X, + which is known to be in operand 0. */ + +const char * +output_call (rtx x) +{ + if (symbolic_operand (x, VOIDmode)) + return m68k_symbolic_call; + else + return "jsr %a0"; +} + +/* Likewise sibling calls. */ + +const char * +output_sibcall (rtx x) +{ + if (symbolic_operand (x, VOIDmode)) + return m68k_symbolic_jump; + else + return "jmp %a0"; +} + +#ifdef M68K_TARGET_COFF + /* Output assembly to switch to section NAME with attribute FLAGS. */ static void -m68k_coff_asm_named_section (name, flags) - const char *name; - unsigned int flags; +m68k_coff_asm_named_section (const char *name, unsigned int flags, + tree decl ATTRIBUTE_UNUSED) { char flagchar; @@ -4246,18 +4354,1442 @@ m68k_coff_asm_named_section (name, flags) fprintf (asm_out_file, "\t.section\t%s,\"%c\"\n", name, flagchar); } -#ifdef CTOR_LIST_BEGIN +#endif /* M68K_TARGET_COFF */ + static void -m68k_svr3_asm_out_constructor (symbol, priority) - rtx symbol; - int priority ATTRIBUTE_UNUSED; +m68k_output_mi_thunk (FILE *file, tree thunk ATTRIBUTE_UNUSED, + HOST_WIDE_INT delta, HOST_WIDE_INT vcall_offset, + tree function) +{ + rtx this_slot, offset, addr, mem, insn; + + /* Pretend to be a post-reload pass while generating rtl. */ + reload_completed = 1; + + /* The "this" pointer is stored at 4(%sp). */ + this_slot = gen_rtx_MEM (Pmode, plus_constant (stack_pointer_rtx, 4)); + + /* Add DELTA to THIS. */ + if (delta != 0) + { + /* Make the offset a legitimate operand for memory addition. */ + offset = GEN_INT (delta); + if ((delta < -8 || delta > 8) + && (TARGET_COLDFIRE || USE_MOVQ (delta))) + { + emit_move_insn (gen_rtx_REG (Pmode, D0_REG), offset); + offset = gen_rtx_REG (Pmode, D0_REG); + } + emit_insn (gen_add3_insn (copy_rtx (this_slot), + copy_rtx (this_slot), offset)); + } + + /* If needed, add *(*THIS + VCALL_OFFSET) to THIS. */ + if (vcall_offset != 0) + { + /* Set the static chain register to *THIS. */ + emit_move_insn (static_chain_rtx, this_slot); + emit_move_insn (static_chain_rtx, gen_rtx_MEM (Pmode, static_chain_rtx)); + + /* Set ADDR to a legitimate address for *THIS + VCALL_OFFSET. */ + addr = plus_constant (static_chain_rtx, vcall_offset); + if (!m68k_legitimate_address_p (Pmode, addr, true)) + { + emit_insn (gen_rtx_SET (VOIDmode, static_chain_rtx, addr)); + addr = static_chain_rtx; + } + + /* Load the offset into %d0 and add it to THIS. */ + emit_move_insn (gen_rtx_REG (Pmode, D0_REG), + gen_rtx_MEM (Pmode, addr)); + emit_insn (gen_add3_insn (copy_rtx (this_slot), + copy_rtx (this_slot), + gen_rtx_REG (Pmode, D0_REG))); + } + + /* Jump to the target function. Use a sibcall if direct jumps are + allowed, otherwise load the address into a register first. */ + mem = DECL_RTL (function); + if (!sibcall_operand (XEXP (mem, 0), VOIDmode)) + { + gcc_assert (flag_pic); + + if (!TARGET_SEP_DATA) + { + /* Use the static chain register as a temporary (call-clobbered) + GOT pointer for this function. We can use the static chain + register because it isn't live on entry to the thunk. */ + SET_REGNO (pic_offset_table_rtx, STATIC_CHAIN_REGNUM); + emit_insn (gen_load_got (pic_offset_table_rtx)); + } + legitimize_pic_address (XEXP (mem, 0), Pmode, static_chain_rtx); + mem = replace_equiv_address (mem, static_chain_rtx); + } + insn = emit_call_insn (gen_sibcall (mem, const0_rtx)); + SIBLING_CALL_P (insn) = 1; + + /* Run just enough of rest_of_compilation. */ + insn = get_insns (); + split_all_insns_noflow (); + final_start_function (insn, file, 1); + final (insn, file, 1); + final_end_function (); + + /* Clean up the vars set above. */ + reload_completed = 0; + + /* Restore the original PIC register. */ + if (flag_pic) + SET_REGNO (pic_offset_table_rtx, PIC_REG); + free_after_compilation (cfun); +} + +/* Worker function for TARGET_STRUCT_VALUE_RTX. */ + +static rtx +m68k_struct_value_rtx (tree fntype ATTRIBUTE_UNUSED, + int incoming ATTRIBUTE_UNUSED) +{ + return gen_rtx_REG (Pmode, M68K_STRUCT_VALUE_REGNUM); +} + +/* Return nonzero if register old_reg can be renamed to register new_reg. */ +int +m68k_hard_regno_rename_ok (unsigned int old_reg ATTRIBUTE_UNUSED, + unsigned int new_reg) +{ + + /* Interrupt functions can only use registers that have already been + saved by the prologue, even if they would normally be + call-clobbered. */ + + if ((m68k_get_function_kind (current_function_decl) + == m68k_fk_interrupt_handler) + && !df_regs_ever_live_p (new_reg)) + return 0; + + return 1; +} + +/* Value is true if hard register REGNO can hold a value of machine-mode + MODE. On the 68000, we let the cpu registers can hold any mode, but + restrict the 68881 registers to floating-point modes. */ + +bool +m68k_regno_mode_ok (int regno, enum machine_mode mode) +{ + if (DATA_REGNO_P (regno)) + { + /* Data Registers, can hold aggregate if fits in. */ + if (regno + GET_MODE_SIZE (mode) / 4 <= 8) + return true; + } + else if (ADDRESS_REGNO_P (regno)) + { + if (regno + GET_MODE_SIZE (mode) / 4 <= 16) + return true; + } + else if (FP_REGNO_P (regno)) + { + /* FPU registers, hold float or complex float of long double or + smaller. */ + if ((GET_MODE_CLASS (mode) == MODE_FLOAT + || GET_MODE_CLASS (mode) == MODE_COMPLEX_FLOAT) + && GET_MODE_UNIT_SIZE (mode) <= TARGET_FP_REG_SIZE) + return true; + } + return false; +} + +/* Implement SECONDARY_RELOAD_CLASS. */ + +enum reg_class +m68k_secondary_reload_class (enum reg_class rclass, + enum machine_mode mode, rtx x) +{ + int regno; + + regno = true_regnum (x); + + /* If one operand of a movqi is an address register, the other + operand must be a general register or constant. Other types + of operand must be reloaded through a data register. */ + if (GET_MODE_SIZE (mode) == 1 + && reg_classes_intersect_p (rclass, ADDR_REGS) + && !(INT_REGNO_P (regno) || CONSTANT_P (x))) + return DATA_REGS; + + /* PC-relative addresses must be loaded into an address register first. */ + if (TARGET_PCREL + && !reg_class_subset_p (rclass, ADDR_REGS) + && symbolic_operand (x, VOIDmode)) + return ADDR_REGS; + + return NO_REGS; +} + +/* Implement PREFERRED_RELOAD_CLASS. */ + +enum reg_class +m68k_preferred_reload_class (rtx x, enum reg_class rclass) +{ + enum reg_class secondary_class; + + /* If RCLASS might need a secondary reload, try restricting it to + a class that doesn't. */ + secondary_class = m68k_secondary_reload_class (rclass, GET_MODE (x), x); + if (secondary_class != NO_REGS + && reg_class_subset_p (secondary_class, rclass)) + return secondary_class; + + /* Prefer to use moveq for in-range constants. */ + if (GET_CODE (x) == CONST_INT + && reg_class_subset_p (DATA_REGS, rclass) + && IN_RANGE (INTVAL (x), -0x80, 0x7f)) + return DATA_REGS; + + /* ??? Do we really need this now? */ + if (GET_CODE (x) == CONST_DOUBLE + && GET_MODE_CLASS (GET_MODE (x)) == MODE_FLOAT) + { + if (TARGET_HARD_FLOAT && reg_class_subset_p (FP_REGS, rclass)) + return FP_REGS; + + return NO_REGS; + } + + return rclass; +} + +/* Return floating point values in a 68881 register. This makes 68881 code + a little bit faster. It also makes -msoft-float code incompatible with + hard-float code, so people have to be careful not to mix the two. + For ColdFire it was decided the ABI incompatibility is undesirable. + If there is need for a hard-float ABI it is probably worth doing it + properly and also passing function arguments in FP registers. */ +rtx +m68k_libcall_value (enum machine_mode mode) +{ + switch (mode) { + case SFmode: + case DFmode: + case XFmode: + if (TARGET_68881) + return gen_rtx_REG (mode, FP0_REG); + break; + default: + break; + } + return gen_rtx_REG (mode, D0_REG); +} + +rtx +m68k_function_value (const_tree valtype, const_tree func ATTRIBUTE_UNUSED) +{ + enum machine_mode mode; + + mode = TYPE_MODE (valtype); + switch (mode) { + case SFmode: + case DFmode: + case XFmode: + if (TARGET_68881) + return gen_rtx_REG (mode, FP0_REG); + break; + default: + break; + } + + /* If the function returns a pointer, push that into %a0. */ + if (func && POINTER_TYPE_P (TREE_TYPE (TREE_TYPE (func)))) + /* For compatibility with the large body of existing code which + does not always properly declare external functions returning + pointer types, the m68k/SVR4 convention is to copy the value + returned for pointer functions from a0 to d0 in the function + epilogue, so that callers that have neglected to properly + declare the callee can still find the correct return value in + d0. */ + return gen_rtx_PARALLEL + (mode, + gen_rtvec (2, + gen_rtx_EXPR_LIST (VOIDmode, + gen_rtx_REG (mode, A0_REG), + const0_rtx), + gen_rtx_EXPR_LIST (VOIDmode, + gen_rtx_REG (mode, D0_REG), + const0_rtx))); + else if (POINTER_TYPE_P (valtype)) + return gen_rtx_REG (mode, A0_REG); + else + return gen_rtx_REG (mode, D0_REG); +} + +/* Worker function for TARGET_RETURN_IN_MEMORY. */ +#if M68K_HONOR_TARGET_STRICT_ALIGNMENT +static bool +m68k_return_in_memory (const_tree type, const_tree fntype ATTRIBUTE_UNUSED) { - rtx xop[2]; + enum machine_mode mode = TYPE_MODE (type); + + if (mode == BLKmode) + return true; - xop[1] = symbol; - xop[0] = gen_rtx_MEM (SImode, gen_rtx_PRE_DEC (SImode, stack_pointer_rtx)); + /* If TYPE's known alignment is less than the alignment of MODE that + would contain the structure, then return in memory. We need to + do so to maintain the compatibility between code compiled with + -mstrict-align and that compiled with -mno-strict-align. */ + if (AGGREGATE_TYPE_P (type) + && TYPE_ALIGN (type) < GET_MODE_ALIGNMENT (mode)) + return true; - init_section (); - output_asm_insn (output_move_simode (xop), xop); + return false; } #endif + +/* CPU to schedule the program for. */ +enum attr_cpu m68k_sched_cpu; + +/* MAC to schedule the program for. */ +enum attr_mac m68k_sched_mac; + +/* Operand type. */ +enum attr_op_type + { + /* No operand. */ + OP_TYPE_NONE, + + /* Integer register. */ + OP_TYPE_RN, + + /* FP register. */ + OP_TYPE_FPN, + + /* Implicit mem reference (e.g. stack). */ + OP_TYPE_MEM1, + + /* Memory without offset or indexing. EA modes 2, 3 and 4. */ + OP_TYPE_MEM234, + + /* Memory with offset but without indexing. EA mode 5. */ + OP_TYPE_MEM5, + + /* Memory with indexing. EA mode 6. */ + OP_TYPE_MEM6, + + /* Memory referenced by absolute address. EA mode 7. */ + OP_TYPE_MEM7, + + /* Immediate operand that doesn't require extension word. */ + OP_TYPE_IMM_Q, + + /* Immediate 16 bit operand. */ + OP_TYPE_IMM_W, + + /* Immediate 32 bit operand. */ + OP_TYPE_IMM_L + }; + +/* Return type of memory ADDR_RTX refers to. */ +static enum attr_op_type +sched_address_type (enum machine_mode mode, rtx addr_rtx) +{ + struct m68k_address address; + + if (symbolic_operand (addr_rtx, VOIDmode)) + return OP_TYPE_MEM7; + + if (!m68k_decompose_address (mode, addr_rtx, + reload_completed, &address)) + { + gcc_assert (!reload_completed); + /* Reload will likely fix the address to be in the register. */ + return OP_TYPE_MEM234; + } + + if (address.scale != 0) + return OP_TYPE_MEM6; + + if (address.base != NULL_RTX) + { + if (address.offset == NULL_RTX) + return OP_TYPE_MEM234; + + return OP_TYPE_MEM5; + } + + gcc_assert (address.offset != NULL_RTX); + + return OP_TYPE_MEM7; +} + +/* Return X or Y (depending on OPX_P) operand of INSN. */ +static rtx +sched_get_operand (rtx insn, bool opx_p) +{ + int i; + + if (recog_memoized (insn) < 0) + gcc_unreachable (); + + extract_constrain_insn_cached (insn); + + if (opx_p) + i = get_attr_opx (insn); + else + i = get_attr_opy (insn); + + if (i >= recog_data.n_operands) + return NULL; + + return recog_data.operand[i]; +} + +/* Return type of INSN's operand X (if OPX_P) or operand Y (if !OPX_P). + If ADDRESS_P is true, return type of memory location operand refers to. */ +static enum attr_op_type +sched_attr_op_type (rtx insn, bool opx_p, bool address_p) +{ + rtx op; + + op = sched_get_operand (insn, opx_p); + + if (op == NULL) + { + gcc_assert (!reload_completed); + return OP_TYPE_RN; + } + + if (address_p) + return sched_address_type (QImode, op); + + if (memory_operand (op, VOIDmode)) + return sched_address_type (GET_MODE (op), XEXP (op, 0)); + + if (register_operand (op, VOIDmode)) + { + if ((!reload_completed && FLOAT_MODE_P (GET_MODE (op))) + || (reload_completed && FP_REG_P (op))) + return OP_TYPE_FPN; + + return OP_TYPE_RN; + } + + if (GET_CODE (op) == CONST_INT) + { + int ival; + + ival = INTVAL (op); + + /* Check for quick constants. */ + switch (get_attr_type (insn)) + { + case TYPE_ALUQ_L: + if (IN_RANGE (ival, 1, 8) || IN_RANGE (ival, -8, -1)) + return OP_TYPE_IMM_Q; + + gcc_assert (!reload_completed); + break; + + case TYPE_MOVEQ_L: + if (USE_MOVQ (ival)) + return OP_TYPE_IMM_Q; + + gcc_assert (!reload_completed); + break; + + case TYPE_MOV3Q_L: + if (valid_mov3q_const (ival)) + return OP_TYPE_IMM_Q; + + gcc_assert (!reload_completed); + break; + + default: + break; + } + + if (IN_RANGE (ival, -0x8000, 0x7fff)) + return OP_TYPE_IMM_W; + + return OP_TYPE_IMM_L; + } + + if (GET_CODE (op) == CONST_DOUBLE) + { + switch (GET_MODE (op)) + { + case SFmode: + return OP_TYPE_IMM_W; + + case VOIDmode: + case DFmode: + return OP_TYPE_IMM_L; + + default: + gcc_unreachable (); + } + } + + if (GET_CODE (op) == CONST + || symbolic_operand (op, VOIDmode) + || LABEL_P (op)) + { + switch (GET_MODE (op)) + { + case QImode: + return OP_TYPE_IMM_Q; + + case HImode: + return OP_TYPE_IMM_W; + + case SImode: + return OP_TYPE_IMM_L; + + default: + if (GET_CODE (op) == SYMBOL_REF) + /* ??? Just a guess. Probably we can guess better using length + attribute of the instructions. */ + return OP_TYPE_IMM_W; + + return OP_TYPE_IMM_L; + } + } + + gcc_assert (!reload_completed); + + if (FLOAT_MODE_P (GET_MODE (op))) + return OP_TYPE_FPN; + + return OP_TYPE_RN; +} + +/* Implement opx_type attribute. + Return type of INSN's operand X. + If ADDRESS_P is true, return type of memory location operand refers to. */ +enum attr_opx_type +m68k_sched_attr_opx_type (rtx insn, int address_p) +{ + switch (sched_attr_op_type (insn, true, address_p != 0)) + { + case OP_TYPE_RN: + return OPX_TYPE_RN; + + case OP_TYPE_FPN: + return OPX_TYPE_FPN; + + case OP_TYPE_MEM1: + return OPX_TYPE_MEM1; + + case OP_TYPE_MEM234: + return OPX_TYPE_MEM234; + + case OP_TYPE_MEM5: + return OPX_TYPE_MEM5; + + case OP_TYPE_MEM6: + return OPX_TYPE_MEM6; + + case OP_TYPE_MEM7: + return OPX_TYPE_MEM7; + + case OP_TYPE_IMM_Q: + return OPX_TYPE_IMM_Q; + + case OP_TYPE_IMM_W: + return OPX_TYPE_IMM_W; + + case OP_TYPE_IMM_L: + return OPX_TYPE_IMM_L; + + default: + gcc_unreachable (); + return 0; + } +} + +/* Implement opy_type attribute. + Return type of INSN's operand Y. + If ADDRESS_P is true, return type of memory location operand refers to. */ +enum attr_opy_type +m68k_sched_attr_opy_type (rtx insn, int address_p) +{ + switch (sched_attr_op_type (insn, false, address_p != 0)) + { + case OP_TYPE_RN: + return OPY_TYPE_RN; + + case OP_TYPE_FPN: + return OPY_TYPE_FPN; + + case OP_TYPE_MEM1: + return OPY_TYPE_MEM1; + + case OP_TYPE_MEM234: + return OPY_TYPE_MEM234; + + case OP_TYPE_MEM5: + return OPY_TYPE_MEM5; + + case OP_TYPE_MEM6: + return OPY_TYPE_MEM6; + + case OP_TYPE_MEM7: + return OPY_TYPE_MEM7; + + case OP_TYPE_IMM_Q: + return OPY_TYPE_IMM_Q; + + case OP_TYPE_IMM_W: + return OPY_TYPE_IMM_W; + + case OP_TYPE_IMM_L: + return OPY_TYPE_IMM_L; + + default: + gcc_unreachable (); + return 0; + } +} + +/* Return size of INSN as int. */ +static int +sched_get_attr_size_int (rtx insn) +{ + int size; + + switch (get_attr_type (insn)) + { + case TYPE_IGNORE: + /* There should be no references to m68k_sched_attr_size for 'ignore' + instructions. */ + gcc_unreachable (); + return 0; + + case TYPE_MUL_L: + size = 2; + break; + + default: + size = 1; + break; + } + + switch (get_attr_opx_type (insn)) + { + case OPX_TYPE_NONE: + case OPX_TYPE_RN: + case OPX_TYPE_FPN: + case OPX_TYPE_MEM1: + case OPX_TYPE_MEM234: + case OPY_TYPE_IMM_Q: + break; + + case OPX_TYPE_MEM5: + case OPX_TYPE_MEM6: + /* Here we assume that most absolute references are short. */ + case OPX_TYPE_MEM7: + case OPY_TYPE_IMM_W: + ++size; + break; + + case OPY_TYPE_IMM_L: + size += 2; + break; + + default: + gcc_unreachable (); + } + + switch (get_attr_opy_type (insn)) + { + case OPY_TYPE_NONE: + case OPY_TYPE_RN: + case OPY_TYPE_FPN: + case OPY_TYPE_MEM1: + case OPY_TYPE_MEM234: + case OPY_TYPE_IMM_Q: + break; + + case OPY_TYPE_MEM5: + case OPY_TYPE_MEM6: + /* Here we assume that most absolute references are short. */ + case OPY_TYPE_MEM7: + case OPY_TYPE_IMM_W: + ++size; + break; + + case OPY_TYPE_IMM_L: + size += 2; + break; + + default: + gcc_unreachable (); + } + + if (size > 3) + { + gcc_assert (!reload_completed); + + size = 3; + } + + return size; +} + +/* Return size of INSN as attribute enum value. */ +enum attr_size +m68k_sched_attr_size (rtx insn) +{ + switch (sched_get_attr_size_int (insn)) + { + case 1: + return SIZE_1; + + case 2: + return SIZE_2; + + case 3: + return SIZE_3; + + default: + gcc_unreachable (); + return 0; + } +} + +/* Return operand X or Y (depending on OPX_P) of INSN, + if it is a MEM, or NULL overwise. */ +static enum attr_op_type +sched_get_opxy_mem_type (rtx insn, bool opx_p) +{ + if (opx_p) + { + switch (get_attr_opx_type (insn)) + { + case OPX_TYPE_NONE: + case OPX_TYPE_RN: + case OPX_TYPE_FPN: + case OPX_TYPE_IMM_Q: + case OPX_TYPE_IMM_W: + case OPX_TYPE_IMM_L: + return OP_TYPE_RN; + + case OPX_TYPE_MEM1: + case OPX_TYPE_MEM234: + case OPX_TYPE_MEM5: + case OPX_TYPE_MEM7: + return OP_TYPE_MEM1; + + case OPX_TYPE_MEM6: + return OP_TYPE_MEM6; + + default: + gcc_unreachable (); + return 0; + } + } + else + { + switch (get_attr_opy_type (insn)) + { + case OPY_TYPE_NONE: + case OPY_TYPE_RN: + case OPY_TYPE_FPN: + case OPY_TYPE_IMM_Q: + case OPY_TYPE_IMM_W: + case OPY_TYPE_IMM_L: + return OP_TYPE_RN; + + case OPY_TYPE_MEM1: + case OPY_TYPE_MEM234: + case OPY_TYPE_MEM5: + case OPY_TYPE_MEM7: + return OP_TYPE_MEM1; + + case OPY_TYPE_MEM6: + return OP_TYPE_MEM6; + + default: + gcc_unreachable (); + return 0; + } + } +} + +/* Implement op_mem attribute. */ +enum attr_op_mem +m68k_sched_attr_op_mem (rtx insn) +{ + enum attr_op_type opx; + enum attr_op_type opy; + + opx = sched_get_opxy_mem_type (insn, true); + opy = sched_get_opxy_mem_type (insn, false); + + if (opy == OP_TYPE_RN && opx == OP_TYPE_RN) + return OP_MEM_00; + + if (opy == OP_TYPE_RN && opx == OP_TYPE_MEM1) + { + switch (get_attr_opx_access (insn)) + { + case OPX_ACCESS_R: + return OP_MEM_10; + + case OPX_ACCESS_W: + return OP_MEM_01; + + case OPX_ACCESS_RW: + return OP_MEM_11; + + default: + gcc_unreachable (); + return 0; + } + } + + if (opy == OP_TYPE_RN && opx == OP_TYPE_MEM6) + { + switch (get_attr_opx_access (insn)) + { + case OPX_ACCESS_R: + return OP_MEM_I0; + + case OPX_ACCESS_W: + return OP_MEM_0I; + + case OPX_ACCESS_RW: + return OP_MEM_I1; + + default: + gcc_unreachable (); + return 0; + } + } + + if (opy == OP_TYPE_MEM1 && opx == OP_TYPE_RN) + return OP_MEM_10; + + if (opy == OP_TYPE_MEM1 && opx == OP_TYPE_MEM1) + { + switch (get_attr_opx_access (insn)) + { + case OPX_ACCESS_W: + return OP_MEM_11; + + default: + gcc_assert (!reload_completed); + return OP_MEM_11; + } + } + + if (opy == OP_TYPE_MEM1 && opx == OP_TYPE_MEM6) + { + switch (get_attr_opx_access (insn)) + { + case OPX_ACCESS_W: + return OP_MEM_1I; + + default: + gcc_assert (!reload_completed); + return OP_MEM_1I; + } + } + + if (opy == OP_TYPE_MEM6 && opx == OP_TYPE_RN) + return OP_MEM_I0; + + if (opy == OP_TYPE_MEM6 && opx == OP_TYPE_MEM1) + { + switch (get_attr_opx_access (insn)) + { + case OPX_ACCESS_W: + return OP_MEM_I1; + + default: + gcc_assert (!reload_completed); + return OP_MEM_I1; + } + } + + gcc_assert (opy == OP_TYPE_MEM6 && opx == OP_TYPE_MEM6); + gcc_assert (!reload_completed); + return OP_MEM_I1; +} + +/* Jump instructions types. Indexed by INSN_UID. + The same rtl insn can be expanded into different asm instructions + depending on the cc0_status. To properly determine type of jump + instructions we scan instruction stream and map jumps types to this + array. */ +static enum attr_type *sched_branch_type; + +/* Return the type of the jump insn. */ +enum attr_type +m68k_sched_branch_type (rtx insn) +{ + enum attr_type type; + + type = sched_branch_type[INSN_UID (insn)]; + + gcc_assert (type != 0); + + return type; +} + +/* Data for ColdFire V4 index bypass. + Producer modifies register that is used as index in consumer with + specified scale. */ +static struct +{ + /* Producer instruction. */ + rtx pro; + + /* Consumer instruction. */ + rtx con; + + /* Scale of indexed memory access within consumer. + Or zero if bypass should not be effective at the moment. */ + int scale; +} sched_cfv4_bypass_data; + +/* An empty state that is used in m68k_sched_adjust_cost. */ +static state_t sched_adjust_cost_state; + +/* Implement adjust_cost scheduler hook. + Return adjusted COST of dependency LINK between DEF_INSN and INSN. */ +static int +m68k_sched_adjust_cost (rtx insn, rtx link ATTRIBUTE_UNUSED, rtx def_insn, + int cost) +{ + int delay; + + if (recog_memoized (def_insn) < 0 + || recog_memoized (insn) < 0) + return cost; + + if (sched_cfv4_bypass_data.scale == 1) + /* Handle ColdFire V4 bypass for indexed address with 1x scale. */ + { + /* haifa-sched.c: insn_cost () calls bypass_p () just before + targetm.sched.adjust_cost (). Hence, we can be relatively sure + that the data in sched_cfv4_bypass_data is up to date. */ + gcc_assert (sched_cfv4_bypass_data.pro == def_insn + && sched_cfv4_bypass_data.con == insn); + + if (cost < 3) + cost = 3; + + sched_cfv4_bypass_data.pro = NULL; + sched_cfv4_bypass_data.con = NULL; + sched_cfv4_bypass_data.scale = 0; + } + else + gcc_assert (sched_cfv4_bypass_data.pro == NULL + && sched_cfv4_bypass_data.con == NULL + && sched_cfv4_bypass_data.scale == 0); + + /* Don't try to issue INSN earlier than DFA permits. + This is especially useful for instructions that write to memory, + as their true dependence (default) latency is better to be set to 0 + to workaround alias analysis limitations. + This is, in fact, a machine independent tweak, so, probably, + it should be moved to haifa-sched.c: insn_cost (). */ + delay = min_insn_conflict_delay (sched_adjust_cost_state, def_insn, insn); + if (delay > cost) + cost = delay; + + return cost; +} + +/* Return maximal number of insns that can be scheduled on a single cycle. */ +static int +m68k_sched_issue_rate (void) +{ + switch (m68k_sched_cpu) + { + case CPU_CFV1: + case CPU_CFV2: + case CPU_CFV3: + return 1; + + case CPU_CFV4: + return 2; + + default: + gcc_unreachable (); + return 0; + } +} + +/* Maximal length of instruction for current CPU. + E.g. it is 3 for any ColdFire core. */ +static int max_insn_size; + +/* Data to model instruction buffer of CPU. */ +struct _sched_ib +{ + /* True if instruction buffer model is modeled for current CPU. */ + bool enabled_p; + + /* Size of the instruction buffer in words. */ + int size; + + /* Number of filled words in the instruction buffer. */ + int filled; + + /* Additional information about instruction buffer for CPUs that have + a buffer of instruction records, rather then a plain buffer + of instruction words. */ + struct _sched_ib_records + { + /* Size of buffer in records. */ + int n_insns; + + /* Array to hold data on adjustements made to the size of the buffer. */ + int *adjust; + + /* Index of the above array. */ + int adjust_index; + } records; + + /* An insn that reserves (marks empty) one word in the instruction buffer. */ + rtx insn; +}; + +static struct _sched_ib sched_ib; + +/* ID of memory unit. */ +static int sched_mem_unit_code; + +/* Implementation of the targetm.sched.variable_issue () hook. + It is called after INSN was issued. It returns the number of insns + that can possibly get scheduled on the current cycle. + It is used here to determine the effect of INSN on the instruction + buffer. */ +static int +m68k_sched_variable_issue (FILE *sched_dump ATTRIBUTE_UNUSED, + int sched_verbose ATTRIBUTE_UNUSED, + rtx insn, int can_issue_more) +{ + int insn_size; + + if (recog_memoized (insn) >= 0 && get_attr_type (insn) != TYPE_IGNORE) + { + switch (m68k_sched_cpu) + { + case CPU_CFV1: + case CPU_CFV2: + insn_size = sched_get_attr_size_int (insn); + break; + + case CPU_CFV3: + insn_size = sched_get_attr_size_int (insn); + + /* ColdFire V3 and V4 cores have instruction buffers that can + accumulate up to 8 instructions regardless of instructions' + sizes. So we should take care not to "prefetch" 24 one-word + or 12 two-words instructions. + To model this behavior we temporarily decrease size of the + buffer by (max_insn_size - insn_size) for next 7 instructions. */ + { + int adjust; + + adjust = max_insn_size - insn_size; + sched_ib.size -= adjust; + + if (sched_ib.filled > sched_ib.size) + sched_ib.filled = sched_ib.size; + + sched_ib.records.adjust[sched_ib.records.adjust_index] = adjust; + } + + ++sched_ib.records.adjust_index; + if (sched_ib.records.adjust_index == sched_ib.records.n_insns) + sched_ib.records.adjust_index = 0; + + /* Undo adjustement we did 7 instructions ago. */ + sched_ib.size + += sched_ib.records.adjust[sched_ib.records.adjust_index]; + + break; + + case CPU_CFV4: + gcc_assert (!sched_ib.enabled_p); + insn_size = 0; + break; + + default: + gcc_unreachable (); + } + + gcc_assert (insn_size <= sched_ib.filled); + --can_issue_more; + } + else if (GET_CODE (PATTERN (insn)) == ASM_INPUT + || asm_noperands (PATTERN (insn)) >= 0) + insn_size = sched_ib.filled; + else + insn_size = 0; + + sched_ib.filled -= insn_size; + + return can_issue_more; +} + +/* Return how many instructions should scheduler lookahead to choose the + best one. */ +static int +m68k_sched_first_cycle_multipass_dfa_lookahead (void) +{ + return m68k_sched_issue_rate () - 1; +} + +/* Implementation of targetm.sched.md_init_global () hook. + It is invoked once per scheduling pass and is used here + to initialize scheduler constants. */ +static void +m68k_sched_md_init_global (FILE *sched_dump ATTRIBUTE_UNUSED, + int sched_verbose ATTRIBUTE_UNUSED, + int n_insns ATTRIBUTE_UNUSED) +{ + /* Init branch types. */ + { + rtx insn; + + sched_branch_type = XCNEWVEC (enum attr_type, get_max_uid () + 1); + + for (insn = get_insns (); insn != NULL_RTX; insn = NEXT_INSN (insn)) + { + if (JUMP_P (insn)) + /* !!! FIXME: Implement real scan here. */ + sched_branch_type[INSN_UID (insn)] = TYPE_BCC; + } + } + +#ifdef ENABLE_CHECKING + /* Check that all instructions have DFA reservations and + that all instructions can be issued from a clean state. */ + { + rtx insn; + state_t state; + + state = alloca (state_size ()); + + for (insn = get_insns (); insn != NULL_RTX; insn = NEXT_INSN (insn)) + { + if (INSN_P (insn) && recog_memoized (insn) >= 0) + { + gcc_assert (insn_has_dfa_reservation_p (insn)); + + state_reset (state); + if (state_transition (state, insn) >= 0) + gcc_unreachable (); + } + } + } +#endif + + /* Setup target cpu. */ + + /* ColdFire V4 has a set of features to keep its instruction buffer full + (e.g., a separate memory bus for instructions) and, hence, we do not model + buffer for this CPU. */ + sched_ib.enabled_p = (m68k_sched_cpu != CPU_CFV4); + + switch (m68k_sched_cpu) + { + case CPU_CFV4: + sched_ib.filled = 0; + + /* FALLTHRU */ + + case CPU_CFV1: + case CPU_CFV2: + max_insn_size = 3; + sched_ib.records.n_insns = 0; + sched_ib.records.adjust = NULL; + break; + + case CPU_CFV3: + max_insn_size = 3; + sched_ib.records.n_insns = 8; + sched_ib.records.adjust = XNEWVEC (int, sched_ib.records.n_insns); + break; + + default: + gcc_unreachable (); + } + + sched_mem_unit_code = get_cpu_unit_code ("cf_mem1"); + + sched_adjust_cost_state = xmalloc (state_size ()); + state_reset (sched_adjust_cost_state); + + start_sequence (); + emit_insn (gen_ib ()); + sched_ib.insn = get_insns (); + end_sequence (); +} + +/* Scheduling pass is now finished. Free/reset static variables. */ +static void +m68k_sched_md_finish_global (FILE *dump ATTRIBUTE_UNUSED, + int verbose ATTRIBUTE_UNUSED) +{ + sched_ib.insn = NULL; + + free (sched_adjust_cost_state); + sched_adjust_cost_state = NULL; + + sched_mem_unit_code = 0; + + free (sched_ib.records.adjust); + sched_ib.records.adjust = NULL; + sched_ib.records.n_insns = 0; + max_insn_size = 0; + + free (sched_branch_type); + sched_branch_type = NULL; +} + +/* Implementation of targetm.sched.md_init () hook. + It is invoked each time scheduler starts on the new block (basic block or + extended basic block). */ +static void +m68k_sched_md_init (FILE *sched_dump ATTRIBUTE_UNUSED, + int sched_verbose ATTRIBUTE_UNUSED, + int n_insns ATTRIBUTE_UNUSED) +{ + switch (m68k_sched_cpu) + { + case CPU_CFV1: + case CPU_CFV2: + sched_ib.size = 6; + break; + + case CPU_CFV3: + sched_ib.size = sched_ib.records.n_insns * max_insn_size; + + memset (sched_ib.records.adjust, 0, + sched_ib.records.n_insns * sizeof (*sched_ib.records.adjust)); + sched_ib.records.adjust_index = 0; + break; + + case CPU_CFV4: + gcc_assert (!sched_ib.enabled_p); + sched_ib.size = 0; + break; + + default: + gcc_unreachable (); + } + + if (sched_ib.enabled_p) + /* haifa-sched.c: schedule_block () calls advance_cycle () just before + the first cycle. Workaround that. */ + sched_ib.filled = -2; +} + +/* Implementation of targetm.sched.dfa_pre_advance_cycle () hook. + It is invoked just before current cycle finishes and is used here + to track if instruction buffer got its two words this cycle. */ +static void +m68k_sched_dfa_pre_advance_cycle (void) +{ + if (!sched_ib.enabled_p) + return; + + if (!cpu_unit_reservation_p (curr_state, sched_mem_unit_code)) + { + sched_ib.filled += 2; + + if (sched_ib.filled > sched_ib.size) + sched_ib.filled = sched_ib.size; + } +} + +/* Implementation of targetm.sched.dfa_post_advance_cycle () hook. + It is invoked just after new cycle begins and is used here + to setup number of filled words in the instruction buffer so that + instructions which won't have all their words prefetched would be + stalled for a cycle. */ +static void +m68k_sched_dfa_post_advance_cycle (void) +{ + int i; + + if (!sched_ib.enabled_p) + return; + + /* Setup number of prefetched instruction words in the instruction + buffer. */ + i = max_insn_size - sched_ib.filled; + + while (--i >= 0) + { + if (state_transition (curr_state, sched_ib.insn) >= 0) + gcc_unreachable (); + } +} + +/* Return X or Y (depending on OPX_P) operand of INSN, + if it is an integer register, or NULL overwise. */ +static rtx +sched_get_reg_operand (rtx insn, bool opx_p) +{ + rtx op = NULL; + + if (opx_p) + { + if (get_attr_opx_type (insn) == OPX_TYPE_RN) + { + op = sched_get_operand (insn, true); + gcc_assert (op != NULL); + + if (!reload_completed && !REG_P (op)) + return NULL; + } + } + else + { + if (get_attr_opy_type (insn) == OPY_TYPE_RN) + { + op = sched_get_operand (insn, false); + gcc_assert (op != NULL); + + if (!reload_completed && !REG_P (op)) + return NULL; + } + } + + return op; +} + +/* Return true, if X or Y (depending on OPX_P) operand of INSN + is a MEM. */ +static bool +sched_mem_operand_p (rtx insn, bool opx_p) +{ + switch (sched_get_opxy_mem_type (insn, opx_p)) + { + case OP_TYPE_MEM1: + case OP_TYPE_MEM6: + return true; + + default: + return false; + } +} + +/* Return X or Y (depending on OPX_P) operand of INSN, + if it is a MEM, or NULL overwise. */ +static rtx +sched_get_mem_operand (rtx insn, bool must_read_p, bool must_write_p) +{ + bool opx_p; + bool opy_p; + + opx_p = false; + opy_p = false; + + if (must_read_p) + { + opx_p = true; + opy_p = true; + } + + if (must_write_p) + { + opx_p = true; + opy_p = false; + } + + if (opy_p && sched_mem_operand_p (insn, false)) + return sched_get_operand (insn, false); + + if (opx_p && sched_mem_operand_p (insn, true)) + return sched_get_operand (insn, true); + + gcc_unreachable (); + return NULL; +} + +/* Return non-zero if PRO modifies register used as part of + address in CON. */ +int +m68k_sched_address_bypass_p (rtx pro, rtx con) +{ + rtx pro_x; + rtx con_mem_read; + + pro_x = sched_get_reg_operand (pro, true); + if (pro_x == NULL) + return 0; + + con_mem_read = sched_get_mem_operand (con, true, false); + gcc_assert (con_mem_read != NULL); + + if (reg_mentioned_p (pro_x, con_mem_read)) + return 1; + + return 0; +} + +/* Helper function for m68k_sched_indexed_address_bypass_p. + if PRO modifies register used as index in CON, + return scale of indexed memory access in CON. Return zero overwise. */ +static int +sched_get_indexed_address_scale (rtx pro, rtx con) +{ + rtx reg; + rtx mem; + struct m68k_address address; + + reg = sched_get_reg_operand (pro, true); + if (reg == NULL) + return 0; + + mem = sched_get_mem_operand (con, true, false); + gcc_assert (mem != NULL && MEM_P (mem)); + + if (!m68k_decompose_address (GET_MODE (mem), XEXP (mem, 0), reload_completed, + &address)) + gcc_unreachable (); + + if (REGNO (reg) == REGNO (address.index)) + { + gcc_assert (address.scale != 0); + return address.scale; + } + + return 0; +} + +/* Return non-zero if PRO modifies register used + as index with scale 2 or 4 in CON. */ +int +m68k_sched_indexed_address_bypass_p (rtx pro, rtx con) +{ + gcc_assert (sched_cfv4_bypass_data.pro == NULL + && sched_cfv4_bypass_data.con == NULL + && sched_cfv4_bypass_data.scale == 0); + + switch (sched_get_indexed_address_scale (pro, con)) + { + case 1: + /* We can't have a variable latency bypass, so + remember to adjust the insn cost in adjust_cost hook. */ + sched_cfv4_bypass_data.pro = pro; + sched_cfv4_bypass_data.con = con; + sched_cfv4_bypass_data.scale = 1; + return 0; + + case 2: + case 4: + return 1; + + default: + return 0; + } +}