/* Subroutines for insn-output.c for Motorola 68000 family.
Copyright (C) 1987, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000,
- 2001, 2003, 2004, 2005, 2006, 2007, 2008, 2009
+ 2001, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012
Free Software Foundation, Inc.
This file is part of GCC.
#include "function.h"
#include "regs.h"
#include "hard-reg-set.h"
-#include "real.h"
#include "insn-config.h"
#include "conditions.h"
#include "output.h"
#include "insn-attr.h"
#include "recog.h"
-#include "toplev.h"
+#include "diagnostic-core.h"
#include "expr.h"
#include "reload.h"
#include "tm_p.h"
#include "sched-int.h"
#include "insn-codes.h"
#include "ggc.h"
+#include "opts.h"
+#include "optabs.h"
enum reg_class regno_reg_class[] =
{
static void m68k_sched_dfa_post_advance_cycle (void);
static int m68k_sched_first_cycle_multipass_dfa_lookahead (void);
+static bool m68k_can_eliminate (const int, const int);
+static void m68k_conditional_register_usage (void);
static bool m68k_legitimate_address_p (enum machine_mode, rtx, bool);
-static bool m68k_handle_option (size_t, const char *, int);
+static void m68k_option_override (void);
+static void m68k_override_options_after_change (void);
static rtx find_addr_reg (rtx);
static const char *singlemove_string (rtx *);
static void m68k_output_mi_thunk (FILE *, tree, HOST_WIDE_INT,
static bool m68k_ok_for_sibcall_p (tree, tree);
static bool m68k_tls_symbol_p (rtx);
static rtx m68k_legitimize_address (rtx, rtx, enum machine_mode);
-static bool m68k_rtx_costs (rtx, int, int, int *, bool);
+static bool m68k_rtx_costs (rtx, int, int, int, int *, bool);
#if M68K_HONOR_TARGET_STRICT_ALIGNMENT
static bool m68k_return_in_memory (const_tree, const_tree);
#endif
static void m68k_output_dwarf_dtprel (FILE *, int, rtx) ATTRIBUTE_UNUSED;
-\f
-
-/* Specify the identification number of the library being built */
-const char *m68k_library_id_string = "_current_shared_library_a5_offset_";
+static void m68k_trampoline_init (rtx, tree, rtx);
+static int m68k_return_pops_args (tree, tree, int);
+static rtx m68k_delegitimize_address (rtx);
+static void m68k_function_arg_advance (cumulative_args_t, enum machine_mode,
+ const_tree, bool);
+static rtx m68k_function_arg (cumulative_args_t, enum machine_mode,
+ const_tree, bool);
+static bool m68k_cannot_force_const_mem (enum machine_mode mode, rtx x);
+static bool m68k_output_addr_const_extra (FILE *, rtx);
+static void m68k_init_sync_libfuncs (void) ATTRIBUTE_UNUSED;
\f
/* Initialize the GCC target structure. */
#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_OPTION_OVERRIDE
+#define TARGET_OPTION_OVERRIDE m68k_option_override
+
+#undef TARGET_OVERRIDE_OPTIONS_AFTER_CHANGE
+#define TARGET_OVERRIDE_OPTIONS_AFTER_CHANGE m68k_override_options_after_change
#undef TARGET_RTX_COSTS
#define TARGET_RTX_COSTS m68k_rtx_costs
#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
+#define TARGET_CANNOT_FORCE_CONST_MEM m68k_cannot_force_const_mem
#undef TARGET_FUNCTION_OK_FOR_SIBCALL
#define TARGET_FUNCTION_OK_FOR_SIBCALL m68k_ok_for_sibcall_p
#undef TARGET_LEGITIMATE_ADDRESS_P
#define TARGET_LEGITIMATE_ADDRESS_P m68k_legitimate_address_p
+#undef TARGET_CAN_ELIMINATE
+#define TARGET_CAN_ELIMINATE m68k_can_eliminate
+
+#undef TARGET_CONDITIONAL_REGISTER_USAGE
+#define TARGET_CONDITIONAL_REGISTER_USAGE m68k_conditional_register_usage
+
+#undef TARGET_TRAMPOLINE_INIT
+#define TARGET_TRAMPOLINE_INIT m68k_trampoline_init
+
+#undef TARGET_RETURN_POPS_ARGS
+#define TARGET_RETURN_POPS_ARGS m68k_return_pops_args
+
+#undef TARGET_DELEGITIMIZE_ADDRESS
+#define TARGET_DELEGITIMIZE_ADDRESS m68k_delegitimize_address
+
+#undef TARGET_FUNCTION_ARG
+#define TARGET_FUNCTION_ARG m68k_function_arg
+
+#undef TARGET_FUNCTION_ARG_ADVANCE
+#define TARGET_FUNCTION_ARG_ADVANCE m68k_function_arg_advance
+
+#undef TARGET_LEGITIMATE_CONSTANT_P
+#define TARGET_LEGITIMATE_CONSTANT_P m68k_legitimate_constant_p
+
+#undef TARGET_ASM_OUTPUT_ADDR_CONST_EXTRA
+#define TARGET_ASM_OUTPUT_ADDR_CONST_EXTRA m68k_output_addr_const_extra
+
+/* The value stored by TAS. */
+#undef TARGET_ATOMIC_TEST_AND_SET_TRUEVAL
+#define TARGET_ATOMIC_TEST_AND_SET_TRUEVAL 128
+
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 }
+ /* { name, min_len, max_len, decl_req, type_req, fn_type_req, handler,
+ affects_type_identity } */
+ { "interrupt", 0, 0, true, false, false, m68k_handle_fndecl_attribute,
+ false },
+ { "interrupt_handler", 0, 0, true, false, false,
+ m68k_handle_fndecl_attribute, false },
+ { "interrupt_thread", 0, 0, true, false, false,
+ m68k_handle_fndecl_attribute, false },
+ { NULL, 0, 0, false, false, false, NULL, false }
};
struct gcc_target targetm = TARGET_INITIALIZER;
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)
+ | FL_BITFIELD | FL_68881 | FL_CAS)
#define FL_FOR_isa_40 (FL_FOR_isa_20 | FL_ISA_68040)
#define FL_FOR_isa_cpu32 (FL_FOR_isa_10 | FL_ISA_68020)
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) },
+#define M68K_ISA(NAME,DEVICE,MICROARCH,ISA,FLAGS) \
+ { NAME, DEVICE, NULL, u##MICROARCH, ISA, FLAGS },
+#include "m68k-isas.def"
+#undef M68K_ISA
{ NULL, unk_device, NULL, unk_arch, isa_max, 0 }
};
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) },
+#define M68K_MICROARCH(NAME,DEVICE,MICROARCH,ISA,FLAGS) \
+ { NAME, DEVICE, NULL, u##MICROARCH, ISA, FLAGS },
+#include "m68k-microarchs.def"
+#undef M68K_MICROARCH
{ NULL, unk_device, NULL, unk_arch, isa_max, 0 }
};
\f
enum M68K_SYMBOLIC_CALL m68k_symbolic_call_var;
\f
-/* 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. */
+/* Implement TARGET_OPTION_OVERRIDE. */
-static bool
-m68k_handle_option (size_t code, const char *arg, int value)
+static void
+m68k_option_override (void)
{
- 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;
- }
-}
+ const struct m68k_target_selection *entry;
+ unsigned long target_mask;
-/* Sometimes certain combinations of command options do not make
- sense on a particular target machine. You can define a macro
- `OVERRIDE_OPTIONS' to take account of this. This macro, if
- defined, is executed once just after all the command options have
- been parsed.
+ if (global_options_set.x_m68k_arch_option)
+ m68k_arch_entry = &all_isas[m68k_arch_option];
- Don't use this macro to turn on various extra optimizations for
- `-O'. That is what `OPTIMIZATION_OPTIONS' is for. */
+ if (global_options_set.x_m68k_cpu_option)
+ m68k_cpu_entry = &all_devices[(int) m68k_cpu_option];
-void
-override_options (void)
-{
- const struct m68k_target_selection *entry;
- unsigned long target_mask;
+ if (global_options_set.x_m68k_tune_option)
+ m68k_tune_entry = &all_microarchs[(int) m68k_tune_option];
/* User can choose:
}
#endif
+ if (stack_limit_rtx != NULL_RTX && !TARGET_68020)
+ {
+ warning (0, "-fstack-limit- options are not supported on this cpu");
+ stack_limit_rtx = NULL_RTX;
+ }
+
SUBTARGET_OVERRIDE_OPTIONS;
/* Setup scheduling options. */
}
}
+/* Implement TARGET_OVERRIDE_OPTIONS_AFTER_CHANGE. */
+
+static void
+m68k_override_options_after_change (void)
+{
+ if (m68k_sched_cpu == CPU_UNKNOWN)
+ {
+ flag_schedule_insns = 0;
+ flag_schedule_insns_after_reload = 0;
+ flag_modulo_sched = 0;
+ }
+}
+
/* 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. */
current_frame.funcdef_no = current_function_funcdef_no;
}
+/* Worker function for TARGET_CAN_ELIMINATE. */
+
+bool
+m68k_can_eliminate (const int from ATTRIBUTE_UNUSED, const int to)
+{
+ return (to == STACK_POINTER_REGNUM ? ! frame_pointer_needed : true);
+}
+
HOST_WIDE_INT
m68k_initial_elimination_offset (int from, int to)
{
if (df_regs_ever_live_p (regno))
return true;
- if (!current_function_is_leaf && call_used_regs[regno])
+ if (!crtl->is_leaf && call_used_regs[regno])
return true;
}
if (adjust_stack_p)
{
- src = plus_constant (base, (count
- * GET_MODE_SIZE (mode)
- * (HOST_WIDE_INT) (store_p ? -1 : 1)));
+ src = plus_constant (Pmode, base,
+ (count
+ * GET_MODE_SIZE (mode)
+ * (HOST_WIDE_INT) (store_p ? -1 : 1)));
XVECEXP (body, 0, i++) = gen_rtx_SET (VOIDmode, base, src);
}
for (; mask != 0; mask >>= 1, regno++)
if (mask & 1)
{
- addr = plus_constant (base, offset);
+ addr = plus_constant (Pmode, base, offset);
operands[!store_p] = gen_frame_mem (mode, addr);
operands[store_p] = gen_rtx_REG (mode, regno);
XVECEXP (body, 0, i++)
m68k_expand_prologue (void)
{
HOST_WIDE_INT fsize_with_regs;
- rtx limit, src, dest, insn;
+ rtx limit, src, dest;
m68k_compute_frame_layout ();
+ if (flag_stack_usage_info)
+ current_function_static_stack_size
+ = current_frame.size + current_frame.offset;
+
/* If the stack limit is a symbol, we can check it here,
before actually allocating the space. */
if (crtl->limit_stack
&& GET_CODE (stack_limit_rtx) == SYMBOL_REF)
{
- limit = plus_constant (stack_limit_rtx, current_frame.size + 4);
- if (!LEGITIMATE_CONSTANT_P (limit))
+ limit = plus_constant (Pmode, stack_limit_rtx, current_frame.size + 4);
+ if (!m68k_legitimate_constant_p (Pmode, limit))
{
emit_move_insn (gen_rtx_REG (Pmode, D0_REG), limit);
limit = gen_rtx_REG (Pmode, D0_REG);
if (!TARGET_SEP_DATA
&& crtl->uses_pic_offset_table)
- insn = emit_insn (gen_load_got (pic_offset_table_rtx));
+ emit_insn (gen_load_got (pic_offset_table_rtx));
}
\f
/* Return true if a simple (return) instruction is sufficient for this
big = false;
restore_from_sp = false;
- /* FIXME : current_function_is_leaf below is too strong.
+ /* FIXME : crtl->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));
+ || (!cfun->calls_alloca && crtl->is_leaf));
/* fsize_with_regs is the size we need to adjust the sp when
popping the frame. */
/* 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);
+ addr = plus_constant (Pmode, addr, -offset);
}
else if (restore_from_sp)
addr = gen_rtx_POST_INC (Pmode, stack_pointer_rtx);
else
- addr = plus_constant (frame_pointer_rtx, -offset);
+ addr = plus_constant (Pmode, frame_pointer_rtx, -offset);
emit_move_insn (gen_rtx_REG (SImode, D0_REG + i),
gen_frame_mem (SImode, addr));
offset -= GET_MODE_SIZE (SImode);
EH_RETURN_STACKADJ_RTX));
if (!sibcall_p)
- emit_jump_insn (gen_rtx_RETURN (VOIDmode));
+ emit_jump_insn (ret_rtx);
}
\f
/* Return true if X is a valid comparison operator for the dbcc
return cc_status.flags & CC_IN_68881;
}
+/* Return true if PARALLEL contains register REGNO. */
+static bool
+m68k_reg_present_p (const_rtx parallel, unsigned int regno)
+{
+ int i;
+
+ if (REG_P (parallel) && REGNO (parallel) == regno)
+ return true;
+
+ if (GET_CODE (parallel) != PARALLEL)
+ return false;
+
+ for (i = 0; i < XVECLEN (parallel, 0); ++i)
+ {
+ const_rtx x;
+
+ x = XEXP (XVECEXP (parallel, 0, i), 0);
+ if (REG_P (x) && REGNO (x) == regno)
+ return true;
+ }
+
+ return false;
+}
+
/* Implement TARGET_FUNCTION_OK_FOR_SIBCALL_P. */
static bool
if (CALL_EXPR_STATIC_CHAIN (exp))
return false;
+ if (!VOID_TYPE_P (TREE_TYPE (DECL_RESULT (cfun->decl))))
+ {
+ /* Check that the return value locations are the same. For
+ example that we aren't returning a value from the sibling in
+ a D0 register but then need to transfer it to a A0 register. */
+ rtx cfun_value;
+ rtx call_value;
+
+ cfun_value = FUNCTION_VALUE (TREE_TYPE (DECL_RESULT (cfun->decl)),
+ cfun->decl);
+ call_value = FUNCTION_VALUE (TREE_TYPE (exp), decl);
+
+ /* Check that the values are equal or that the result the callee
+ function returns is superset of what the current function returns. */
+ if (!(rtx_equal_p (cfun_value, call_value)
+ || (REG_P (cfun_value)
+ && m68k_reg_present_p (call_value, REGNO (cfun_value)))))
+ 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
return false;
}
+/* On the m68k all args are always pushed. */
+
+static rtx
+m68k_function_arg (cumulative_args_t cum ATTRIBUTE_UNUSED,
+ enum machine_mode mode ATTRIBUTE_UNUSED,
+ const_tree type ATTRIBUTE_UNUSED,
+ bool named ATTRIBUTE_UNUSED)
+{
+ return NULL_RTX;
+}
+
+static void
+m68k_function_arg_advance (cumulative_args_t cum_v, enum machine_mode mode,
+ const_tree type, bool named ATTRIBUTE_UNUSED)
+{
+ CUMULATIVE_ARGS *cum = get_cumulative_args (cum_v);
+
+ *cum += (mode != BLKmode
+ ? (GET_MODE_SIZE (mode) + 3) & ~3
+ : (int_size_in_bytes (type) + 3) & ~3);
+}
+
/* Convert X to a legitimate function call memory reference and return the
result. */
However, if REG is a broken-out memory address or multiplication,
nothing needs to be done because REG can certainly go in an address reg. */
-rtx
+static rtx
m68k_legitimize_address (rtx x, rtx oldx, enum machine_mode mode)
{
if (m68k_tls_symbol_p (x))
return m68k_tls_reference_p (x, false);
}
+/* Implement TARGET_CANNOT_FORCE_CONST_MEM. */
+
+static bool
+m68k_cannot_force_const_mem (enum machine_mode mode ATTRIBUTE_UNUSED, rtx x)
+{
+ return m68k_illegitimate_symbolic_constant_p (x);
+}
+
/* 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. */
address));
}
+/* Implement TARGET_LEGITIMATE_CONSTANT_P. */
+
+bool
+m68k_legitimate_constant_p (enum machine_mode mode, rtx x)
+{
+ return mode != XFmode && !m68k_illegitimate_symbolic_constant_p (x);
+}
+
/* Return true if X matches the 'Q' constraint. It must be a memory
with a base address and no constant offset or index. */
base == reg ? 0 : reg);
if (GET_CODE (orig) == CONST_INT)
- pic_ref = plus_constant (base, INTVAL (orig));
+ pic_ref = plus_constant (Pmode, base, INTVAL (orig));
else
pic_ref = gen_rtx_PLUS (Pmode, base, orig);
}
}
static bool
-m68k_rtx_costs (rtx x, int code, int outer_code, int *total,
- bool speed ATTRIBUTE_UNUSED)
+m68k_rtx_costs (rtx x, int code, int outer_code, int opno ATTRIBUTE_UNUSED,
+ int *total, bool speed ATTRIBUTE_UNUSED)
{
switch (code)
{
/* Normal case: do the two words, low-numbered first. */
+ m68k_final_prescan_insn (NULL, operands, 2);
handle_movsi (operands);
/* Do the middle one of the three words for long double */
if (addreg1)
handle_reg_adjust (addreg1, 4);
+ m68k_final_prescan_insn (NULL, middlehalf, 2);
handle_movsi (middlehalf);
}
handle_reg_adjust (addreg1, 4);
/* Do that word. */
+ m68k_final_prescan_insn (NULL, latehalf, 2);
handle_movsi (latehalf);
/* Undo the adds we just did. */
if (scratch_reg
&& reload_in_progress && GET_CODE (operand0) == REG
&& REGNO (operand0) >= FIRST_PSEUDO_REGISTER)
- operand0 = reg_equiv_mem[REGNO (operand0)];
+ operand0 = reg_equiv_mem (REGNO (operand0));
else if (scratch_reg
&& reload_in_progress && GET_CODE (operand0) == SUBREG
&& GET_CODE (SUBREG_REG (operand0)) == REG
/* 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))],
+ reg_equiv_mem (REGNO (SUBREG_REG (operand0))),
SUBREG_BYTE (operand0));
- operand0 = alter_subreg (&temp);
+ operand0 = alter_subreg (&temp, true);
}
if (scratch_reg
&& reload_in_progress && GET_CODE (operand1) == REG
&& REGNO (operand1) >= FIRST_PSEUDO_REGISTER)
- operand1 = reg_equiv_mem[REGNO (operand1)];
+ operand1 = reg_equiv_mem (REGNO (operand1));
else if (scratch_reg
&& reload_in_progress && GET_CODE (operand1) == SUBREG
&& GET_CODE (SUBREG_REG (operand1)) == REG
/* 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))],
+ reg_equiv_mem (REGNO (SUBREG_REG (operand1))),
SUBREG_BYTE (operand1));
- operand1 = alter_subreg (&temp);
+ operand1 = alter_subreg (&temp, true);
}
if (scratch_reg && reload_in_progress && GET_CODE (operand0) == MEM
&& 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)))
+ if (!FP_REG_P (XEXP (cc_status.value2, 0))
+ && FP_REG_P (XEXP (cc_status.value2, 1)))
cc_status.flags |= CC_REVERSED;
}
}
}
}
-/* m68k implementation of OUTPUT_ADDR_CONST_EXTRA. */
+/* m68k implementation of TARGET_OUTPUT_ADDR_CONST_EXTRA. */
-bool
+static bool
m68k_output_addr_const_extra (FILE *file, rtx x)
{
if (GET_CODE (x) == UNSPEC)
case UNSPEC_RELOC16:
case UNSPEC_RELOC32:
output_addr_const (file, XVECEXP (x, 0, 0));
- fputs (m68k_get_reloc_decoration (INTVAL (XVECEXP (x, 0, 1))), file);
+ fputs (m68k_get_reloc_decoration
+ ((enum m68k_reloc) INTVAL (XVECEXP (x, 0, 1))), file);
return true;
default:
fputs ("@TLSLDO+0x8000", file);
}
+/* In the name of slightly smaller debug output, and to cater to
+ general assembler lossage, recognize various UNSPEC sequences
+ and turn them back into a direct symbol reference. */
+
+static rtx
+m68k_delegitimize_address (rtx orig_x)
+{
+ rtx x;
+ struct m68k_address addr;
+ rtx unspec;
+
+ orig_x = delegitimize_mem_from_attrs (orig_x);
+ x = orig_x;
+ if (MEM_P (x))
+ x = XEXP (x, 0);
+
+ if (GET_CODE (x) != PLUS || GET_MODE (x) != Pmode)
+ return orig_x;
+
+ if (!m68k_decompose_address (GET_MODE (x), x, false, &addr)
+ || addr.offset == NULL_RTX
+ || GET_CODE (addr.offset) != CONST)
+ return orig_x;
+
+ unspec = XEXP (addr.offset, 0);
+ if (GET_CODE (unspec) == PLUS && CONST_INT_P (XEXP (unspec, 1)))
+ unspec = XEXP (unspec, 0);
+ if (GET_CODE (unspec) != UNSPEC
+ || (XINT (unspec, 1) != UNSPEC_RELOC16
+ && XINT (unspec, 1) != UNSPEC_RELOC32))
+ return orig_x;
+ x = XVECEXP (unspec, 0, 0);
+ gcc_assert (GET_CODE (x) == SYMBOL_REF || GET_CODE (x) == LABEL_REF);
+ if (unspec != XEXP (addr.offset, 0))
+ x = gen_rtx_PLUS (Pmode, x, XEXP (XEXP (addr.offset, 0), 1));
+ if (addr.index)
+ {
+ rtx idx = addr.index;
+ if (addr.scale != 1)
+ idx = gen_rtx_MULT (Pmode, idx, GEN_INT (addr.scale));
+ x = gen_rtx_PLUS (Pmode, idx, x);
+ }
+ if (addr.base)
+ x = gen_rtx_PLUS (Pmode, addr.base, x);
+ if (MEM_P (orig_x))
+ x = replace_equiv_address_nv (orig_x, x);
+ return x;
+}
+
\f
/* A C compound statement to output to stdio stream STREAM the
assembler syntax for an instruction operand that is a memory
return "and%.w %2,%0";
}
if (GET_CODE (operands[2]) == CONST_INT
- && (logval = exact_log2 (~ INTVAL (operands[2]))) >= 0
+ && (logval = exact_log2 (~ INTVAL (operands[2]) & 0xffffffff)) >= 0
&& (DATA_REG_P (operands[0])
|| offsettable_memref_p (operands[0])))
{
return "or%.w %2,%0";
}
if (GET_CODE (operands[2]) == CONST_INT
- && (logval = exact_log2 (INTVAL (operands[2]))) >= 0
+ && (logval = exact_log2 (INTVAL (operands[2]) & 0xffffffff)) >= 0
&& (DATA_REG_P (operands[0])
|| offsettable_memref_p (operands[0])))
{
return "eor%.w %2,%0";
}
if (GET_CODE (operands[2]) == CONST_INT
- && (logval = exact_log2 (INTVAL (operands[2]))) >= 0
+ && (logval = exact_log2 (INTVAL (operands[2]) & 0xffffffff)) >= 0
&& (DATA_REG_P (operands[0])
|| offsettable_memref_p (operands[0])))
{
HOST_WIDE_INT delta, HOST_WIDE_INT vcall_offset,
tree function)
{
- rtx this_slot, offset, addr, mem, insn;
+ rtx this_slot, offset, addr, mem, insn, tmp;
+
+ /* Avoid clobbering the struct value reg by using the
+ static chain reg as a temporary. */
+ tmp = gen_rtx_REG (Pmode, STATIC_CHAIN_REGNUM);
/* 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));
+ this_slot = gen_rtx_MEM (Pmode, plus_constant (Pmode,
+ stack_pointer_rtx, 4));
/* Add DELTA to THIS. */
if (delta != 0)
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));
+ emit_move_insn (tmp, this_slot);
+ emit_move_insn (tmp, gen_rtx_MEM (Pmode, tmp));
/* Set ADDR to a legitimate address for *THIS + VCALL_OFFSET. */
- addr = plus_constant (static_chain_rtx, vcall_offset);
+ addr = plus_constant (Pmode, tmp, vcall_offset);
if (!m68k_legitimate_address_p (Pmode, addr, true))
{
- emit_insn (gen_rtx_SET (VOIDmode, static_chain_rtx, addr));
- addr = static_chain_rtx;
+ emit_insn (gen_rtx_SET (VOIDmode, tmp, addr));
+ addr = tmp;
}
/* Load the offset into %d0 and add it to THIS. */
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);
+ legitimize_pic_address (XEXP (mem, 0), Pmode, tmp);
+ mem = replace_equiv_address (mem, tmp);
}
insn = emit_call_insn (gen_sibcall (mem, const0_rtx));
SIBLING_CALL_P (insn) = 1;
/* 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. */
return gen_rtx_REG (mode, m68k_libcall_value_in_a0_p ? A0_REG : D0_REG);
}
+/* Location in which function value is returned.
+ NOTE: Due to differences in ABIs, don't call this function directly,
+ use FUNCTION_VALUE instead. */
rtx
m68k_function_value (const_tree valtype, const_tree func ATTRIBUTE_UNUSED)
{
default:
gcc_unreachable ();
- return 0;
}
}
default:
gcc_unreachable ();
- return 0;
}
}
default:
gcc_unreachable ();
- return 0;
}
}
default:
gcc_unreachable ();
- return 0;
}
}
else
default:
gcc_unreachable ();
- return 0;
}
}
}
default:
gcc_unreachable ();
- return 0;
}
}
default:
gcc_unreachable ();
- return 0;
}
}
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. */
gcc_unreachable ();
}
- gcc_assert (insn_size <= sched_ib.filled);
+ if (insn_size > sched_ib.filled)
+ /* Scheduling for register pressure does not always take DFA into
+ account. Workaround instruction buffer not being filled enough. */
+ {
+ gcc_assert (sched_pressure == SCHED_PRESSURE_WEIGHTED);
+ insn_size = sched_ib.filled;
+ }
+
--can_issue_more;
}
else if (GET_CODE (PATTERN (insn)) == ASM_INPUT
return m68k_sched_issue_rate () - 1;
}
-/* Implementation of targetm.sched.md_init_global () hook.
+/* Implementation of targetm.sched.init_global () hook.
It is invoked once per scheduling pass and is used here
to initialize scheduler constants. */
static void
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. */
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.
+/* Implementation of targetm.sched.init () hook.
It is invoked each time scheduler starts on the new block (basic block or
extended basic block). */
static void
while (--i >= 0)
{
if (state_transition (curr_state, sched_ib.insn) >= 0)
- gcc_unreachable ();
+ /* Pick up scheduler state. */
+ ++sched_ib.filled;
}
}
}
}
+/* We generate a two-instructions program at M_TRAMP :
+ movea.l &CHAIN_VALUE,%a0
+ jmp FNADDR
+ where %a0 can be modified by changing STATIC_CHAIN_REGNUM. */
+
+static void
+m68k_trampoline_init (rtx m_tramp, tree fndecl, rtx chain_value)
+{
+ rtx fnaddr = XEXP (DECL_RTL (fndecl), 0);
+ rtx mem;
+
+ gcc_assert (ADDRESS_REGNO_P (STATIC_CHAIN_REGNUM));
+
+ mem = adjust_address (m_tramp, HImode, 0);
+ emit_move_insn (mem, GEN_INT(0x207C + ((STATIC_CHAIN_REGNUM-8) << 9)));
+ mem = adjust_address (m_tramp, SImode, 2);
+ emit_move_insn (mem, chain_value);
+
+ mem = adjust_address (m_tramp, HImode, 6);
+ emit_move_insn (mem, GEN_INT(0x4EF9));
+ mem = adjust_address (m_tramp, SImode, 8);
+ emit_move_insn (mem, fnaddr);
+
+ FINALIZE_TRAMPOLINE (XEXP (m_tramp, 0));
+}
+
+/* On the 68000, the RTS insn cannot pop anything.
+ On the 68010, the RTD insn may be used to pop them if the number
+ of args is fixed, but if the number is variable then the caller
+ must pop them all. RTD can't be used for library calls now
+ because the library is compiled with the Unix compiler.
+ Use of RTD is a selectable option, since it is incompatible with
+ standard Unix calling sequences. If the option is not selected,
+ the caller must always pop the args. */
+
+static int
+m68k_return_pops_args (tree fundecl, tree funtype, int size)
+{
+ return ((TARGET_RTD
+ && (!fundecl
+ || TREE_CODE (fundecl) != IDENTIFIER_NODE)
+ && (!stdarg_p (funtype)))
+ ? size : 0);
+}
+
+/* Make sure everything's fine if we *don't* have a given processor.
+ This assumes that putting a register in fixed_regs will keep the
+ compiler's mitts completely off it. We don't bother to zero it out
+ of register classes. */
+
+static void
+m68k_conditional_register_usage (void)
+{
+ int i;
+ HARD_REG_SET x;
+ if (!TARGET_HARD_FLOAT)
+ {
+ COPY_HARD_REG_SET (x, reg_class_contents[(int)FP_REGS]);
+ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+ if (TEST_HARD_REG_BIT (x, i))
+ fixed_regs[i] = call_used_regs[i] = 1;
+ }
+ if (flag_pic)
+ fixed_regs[PIC_REG] = call_used_regs[PIC_REG] = 1;
+}
+
+static void
+m68k_init_sync_libfuncs (void)
+{
+ init_sync_libfuncs (UNITS_PER_WORD);
+}
+
+/* Implements EPILOGUE_USES. All registers are live on exit from an
+ interrupt routine. */
+bool
+m68k_epilogue_uses (int regno ATTRIBUTE_UNUSED)
+{
+ return (reload_completed
+ && (m68k_get_function_kind (current_function_decl)
+ == m68k_fk_interrupt_handler));
+}
+
#include "gt-m68k.h"