Index: gcc/gcc/config/m68k/m68k.c |
diff --git a/gcc/gcc/config/m68k/m68k.c b/gcc/gcc/config/m68k/m68k.c |
index 2f931c6be428bbfaddcf75d98a24886ec9eda301..c22f3b84f7915a9bcd7fb22d19bc1155ac4fd90f 100644 |
--- a/gcc/gcc/config/m68k/m68k.c |
+++ b/gcc/gcc/config/m68k/m68k.c |
@@ -1,6 +1,6 @@ |
/* 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 |
+ 2001, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 |
Free Software Foundation, Inc. |
This file is part of GCC. |
@@ -46,6 +46,7 @@ along with GCC; see the file COPYING3. If not see |
/* ??? Need to add a dependency between m68k.o and sched-int.h. */ |
#include "sched-int.h" |
#include "insn-codes.h" |
+#include "ggc.h" |
enum reg_class regno_reg_class[] = |
{ |
@@ -131,12 +132,11 @@ 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_can_eliminate (const int, const int); |
+static bool m68k_legitimate_address_p (enum machine_mode, rtx, bool); |
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); |
@@ -146,19 +146,19 @@ static tree m68k_handle_fndecl_attribute (tree *node, tree name, |
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_tls_symbol_p (rtx); |
+static rtx m68k_legitimize_address (rtx, rtx, enum machine_mode); |
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 |
+static void m68k_output_dwarf_dtprel (FILE *, int, rtx) ATTRIBUTE_UNUSED; |
+static void m68k_trampoline_init (rtx, tree, rtx); |
+static rtx m68k_delegitimize_address (rtx); |
/* 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 |
- 68060, which has no fsCC instructions. */ |
-int m68k_last_compare_had_fp_operands; |
/* Initialize the GCC target structure. */ |
@@ -198,6 +198,9 @@ int m68k_last_compare_had_fp_operands; |
#undef TARGET_ASM_FILE_START_APP_OFF |
#define TARGET_ASM_FILE_START_APP_OFF true |
+#undef TARGET_LEGITIMIZE_ADDRESS |
+#define TARGET_LEGITIMIZE_ADDRESS m68k_legitimize_address |
+ |
#undef TARGET_SCHED_ADJUST_COST |
#define TARGET_SCHED_ADJUST_COST m68k_sched_adjust_cost |
@@ -252,6 +255,26 @@ int m68k_last_compare_had_fp_operands; |
#define TARGET_RETURN_IN_MEMORY m68k_return_in_memory |
#endif |
+#ifdef HAVE_AS_TLS |
+#undef TARGET_HAVE_TLS |
+#define TARGET_HAVE_TLS (true) |
+ |
+#undef TARGET_ASM_OUTPUT_DWARF_DTPREL |
+#define TARGET_ASM_OUTPUT_DWARF_DTPREL m68k_output_dwarf_dtprel |
+#endif |
+ |
+#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_TRAMPOLINE_INIT |
+#define TARGET_TRAMPOLINE_INIT m68k_trampoline_init |
+ |
+#undef TARGET_DELEGITIMIZE_ADDRESS |
+#define TARGET_DELEGITIMIZE_ADDRESS m68k_delegitimize_address |
+ |
static const struct attribute_spec m68k_attribute_table[] = |
{ |
/* { name, min_len, max_len, decl_req, type_req, fn_type_req, handler } */ |
@@ -780,8 +803,8 @@ m68k_handle_fndecl_attribute (tree *node, tree name, |
{ |
if (TREE_CODE (*node) != FUNCTION_DECL) |
{ |
- warning (OPT_Wattributes, "%qs attribute only applies to functions", |
- IDENTIFIER_POINTER (name)); |
+ warning (OPT_Wattributes, "%qE attribute only applies to functions", |
+ name); |
*no_add_attrs = true; |
} |
@@ -855,6 +878,14 @@ m68k_compute_frame_layout (void) |
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) |
{ |
@@ -1023,10 +1054,10 @@ m68k_expand_prologue (void) |
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)); |
+ emit_insn (gen_ctrapsi4 (gen_rtx_LTU (VOIDmode, |
+ stack_pointer_rtx, limit), |
+ stack_pointer_rtx, limit, |
+ const1_rtx)); |
} |
fsize_with_regs = current_frame.size; |
@@ -1109,12 +1140,11 @@ m68k_expand_prologue (void) |
if (crtl->limit_stack) |
{ |
if (REG_P (stack_limit_rtx)) |
- { |
- emit_insn (gen_cmpsi (stack_pointer_rtx, stack_limit_rtx)); |
- emit_insn (gen_conditional_trap (gen_rtx_LTU (VOIDmode, |
- cc0_rtx, const0_rtx), |
- const1_rtx)); |
- } |
+ emit_insn (gen_ctrapsi4 (gen_rtx_LTU (VOIDmode, stack_pointer_rtx, |
+ stack_limit_rtx), |
+ stack_pointer_rtx, stack_limit_rtx, |
+ const1_rtx)); |
+ |
else if (GET_CODE (stack_limit_rtx) != SYMBOL_REF) |
warning (0, "stack limit expression is not supported"); |
} |
@@ -1150,8 +1180,7 @@ m68k_expand_prologue (void) |
current_frame.reg_mask, true, true)); |
} |
- if (flag_pic |
- && !TARGET_SEP_DATA |
+ if (!TARGET_SEP_DATA |
&& crtl->uses_pic_offset_table) |
insn = emit_insn (gen_load_got (pic_offset_table_rtx)); |
} |
@@ -1374,6 +1403,30 @@ flags_in_68881 (void) |
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 |
@@ -1386,6 +1439,26 @@ m68k_ok_for_sibcall_p (tree decl, tree exp) |
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 |
@@ -1425,6 +1498,86 @@ m68k_legitimize_sibcall_address (rtx x) |
return replace_equiv_address (x, gen_rtx_REG (Pmode, STATIC_CHAIN_REGNUM)); |
} |
+/* Convert X to a legitimate address and return it if successful. Otherwise |
+ return X. |
+ |
+ For the 68000, we handle X+REG by loading X into a register R and |
+ using R+REG. R will go in an address reg and indexing will be used. |
+ 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. */ |
+ |
+static rtx |
+m68k_legitimize_address (rtx x, rtx oldx, enum machine_mode mode) |
+{ |
+ if (m68k_tls_symbol_p (x)) |
+ return m68k_legitimize_tls_address (x); |
+ |
+ if (GET_CODE (x) == PLUS) |
+ { |
+ int ch = (x) != (oldx); |
+ int copied = 0; |
+ |
+#define COPY_ONCE(Y) if (!copied) { Y = copy_rtx (Y); copied = ch = 1; } |
+ |
+ if (GET_CODE (XEXP (x, 0)) == MULT) |
+ { |
+ COPY_ONCE (x); |
+ XEXP (x, 0) = force_operand (XEXP (x, 0), 0); |
+ } |
+ if (GET_CODE (XEXP (x, 1)) == MULT) |
+ { |
+ COPY_ONCE (x); |
+ XEXP (x, 1) = force_operand (XEXP (x, 1), 0); |
+ } |
+ if (ch) |
+ { |
+ if (GET_CODE (XEXP (x, 1)) == REG |
+ && GET_CODE (XEXP (x, 0)) == REG) |
+ { |
+ if (TARGET_COLDFIRE_FPU && GET_MODE_CLASS (mode) == MODE_FLOAT) |
+ { |
+ COPY_ONCE (x); |
+ x = force_operand (x, 0); |
+ } |
+ return x; |
+ } |
+ if (memory_address_p (mode, x)) |
+ return x; |
+ } |
+ if (GET_CODE (XEXP (x, 0)) == REG |
+ || (GET_CODE (XEXP (x, 0)) == SIGN_EXTEND |
+ && GET_CODE (XEXP (XEXP (x, 0), 0)) == REG |
+ && GET_MODE (XEXP (XEXP (x, 0), 0)) == HImode)) |
+ { |
+ rtx temp = gen_reg_rtx (Pmode); |
+ rtx val = force_operand (XEXP (x, 1), 0); |
+ emit_move_insn (temp, val); |
+ COPY_ONCE (x); |
+ XEXP (x, 1) = temp; |
+ if (TARGET_COLDFIRE_FPU && GET_MODE_CLASS (mode) == MODE_FLOAT |
+ && GET_CODE (XEXP (x, 0)) == REG) |
+ x = force_operand (x, 0); |
+ } |
+ else if (GET_CODE (XEXP (x, 1)) == REG |
+ || (GET_CODE (XEXP (x, 1)) == SIGN_EXTEND |
+ && GET_CODE (XEXP (XEXP (x, 1), 0)) == REG |
+ && GET_MODE (XEXP (XEXP (x, 1), 0)) == HImode)) |
+ { |
+ rtx temp = gen_reg_rtx (Pmode); |
+ rtx val = force_operand (XEXP (x, 0), 0); |
+ emit_move_insn (temp, val); |
+ COPY_ONCE (x); |
+ XEXP (x, 0) = temp; |
+ if (TARGET_COLDFIRE_FPU && GET_MODE_CLASS (mode) == MODE_FLOAT |
+ && GET_CODE (XEXP (x, 1)) == REG) |
+ x = force_operand (x, 0); |
+ } |
+ } |
+ |
+ return x; |
+} |
+ |
+ |
/* 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 |
@@ -1777,7 +1930,7 @@ m68k_illegitimate_symbolic_constant_p (rtx x) |
&& !offset_within_block_p (base, INTVAL (offset))) |
return true; |
} |
- return false; |
+ return m68k_tls_reference_p (x, false); |
} |
/* Return true if X is a legitimate constant address that can reach |
@@ -1805,7 +1958,7 @@ m68k_legitimate_constant_address_p (rtx x, unsigned int reach, bool strict_p) |
return false; |
} |
- return true; |
+ return !m68k_tls_reference_p (x, false); |
} |
/* Return true if X is a LABEL_REF for a jump table. Assume that unplaced |
@@ -1872,15 +2025,17 @@ m68k_decompose_address (enum machine_mode mode, rtx x, |
/* 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)) |
+ if (GET_CODE (x) == PLUS |
+ && XEXP (x, 0) == pic_offset_table_rtx) |
{ |
- address->base = XEXP (x, 0); |
- address->offset = XEXP (x, 1); |
- return true; |
+ /* As we are processing a PLUS, do not unwrap RELOC32 symbols -- |
+ they are invalid in this context. */ |
+ if (m68k_unwrap_symbol (XEXP (x, 1), false) != XEXP (x, 1)) |
+ { |
+ address->base = XEXP (x, 0); |
+ address->offset = XEXP (x, 1); |
+ return true; |
+ } |
} |
/* The ColdFire FPU only accepts addressing modes 2-5. */ |
@@ -2025,6 +2180,243 @@ m68k_matches_u_p (rtx x) |
&& !address.index); |
} |
+/* Return GOT pointer. */ |
+ |
+static rtx |
+m68k_get_gp (void) |
+{ |
+ if (pic_offset_table_rtx == NULL_RTX) |
+ pic_offset_table_rtx = gen_rtx_REG (Pmode, PIC_REG); |
+ |
+ crtl->uses_pic_offset_table = 1; |
+ |
+ return pic_offset_table_rtx; |
+} |
+ |
+/* M68K relocations, used to distinguish GOT and TLS relocations in UNSPEC |
+ wrappers. */ |
+enum m68k_reloc { RELOC_GOT, RELOC_TLSGD, RELOC_TLSLDM, RELOC_TLSLDO, |
+ RELOC_TLSIE, RELOC_TLSLE }; |
+ |
+#define TLS_RELOC_P(RELOC) ((RELOC) != RELOC_GOT) |
+ |
+/* Wrap symbol X into unspec representing relocation RELOC. |
+ BASE_REG - register that should be added to the result. |
+ TEMP_REG - if non-null, temporary register. */ |
+ |
+static rtx |
+m68k_wrap_symbol (rtx x, enum m68k_reloc reloc, rtx base_reg, rtx temp_reg) |
+{ |
+ bool use_x_p; |
+ |
+ use_x_p = (base_reg == pic_offset_table_rtx) ? TARGET_XGOT : TARGET_XTLS; |
+ |
+ if (TARGET_COLDFIRE && use_x_p) |
+ /* When compiling with -mx{got, tls} switch the code will look like this: |
+ |
+ move.l <X>@<RELOC>,<TEMP_REG> |
+ add.l <BASE_REG>,<TEMP_REG> */ |
+ { |
+ /* Wrap X in UNSPEC_??? to tip m68k_output_addr_const_extra |
+ to put @RELOC after reference. */ |
+ x = gen_rtx_UNSPEC (Pmode, gen_rtvec (2, x, GEN_INT (reloc)), |
+ UNSPEC_RELOC32); |
+ x = gen_rtx_CONST (Pmode, x); |
+ |
+ if (temp_reg == NULL) |
+ { |
+ gcc_assert (can_create_pseudo_p ()); |
+ temp_reg = gen_reg_rtx (Pmode); |
+ } |
+ |
+ emit_move_insn (temp_reg, x); |
+ emit_insn (gen_addsi3 (temp_reg, temp_reg, base_reg)); |
+ x = temp_reg; |
+ } |
+ else |
+ { |
+ x = gen_rtx_UNSPEC (Pmode, gen_rtvec (2, x, GEN_INT (reloc)), |
+ UNSPEC_RELOC16); |
+ x = gen_rtx_CONST (Pmode, x); |
+ |
+ x = gen_rtx_PLUS (Pmode, base_reg, x); |
+ } |
+ |
+ return x; |
+} |
+ |
+/* Helper for m68k_unwrap_symbol. |
+ Also, if unwrapping was successful (that is if (ORIG != <return value>)), |
+ sets *RELOC_PTR to relocation type for the symbol. */ |
+ |
+static rtx |
+m68k_unwrap_symbol_1 (rtx orig, bool unwrap_reloc32_p, |
+ enum m68k_reloc *reloc_ptr) |
+{ |
+ if (GET_CODE (orig) == CONST) |
+ { |
+ rtx x; |
+ enum m68k_reloc dummy; |
+ |
+ x = XEXP (orig, 0); |
+ |
+ if (reloc_ptr == NULL) |
+ reloc_ptr = &dummy; |
+ |
+ /* Handle an addend. */ |
+ if ((GET_CODE (x) == PLUS || GET_CODE (x) == MINUS) |
+ && CONST_INT_P (XEXP (x, 1))) |
+ x = XEXP (x, 0); |
+ |
+ if (GET_CODE (x) == UNSPEC) |
+ { |
+ switch (XINT (x, 1)) |
+ { |
+ case UNSPEC_RELOC16: |
+ orig = XVECEXP (x, 0, 0); |
+ *reloc_ptr = (enum m68k_reloc) INTVAL (XVECEXP (x, 0, 1)); |
+ break; |
+ |
+ case UNSPEC_RELOC32: |
+ if (unwrap_reloc32_p) |
+ { |
+ orig = XVECEXP (x, 0, 0); |
+ *reloc_ptr = (enum m68k_reloc) INTVAL (XVECEXP (x, 0, 1)); |
+ } |
+ break; |
+ |
+ default: |
+ break; |
+ } |
+ } |
+ } |
+ |
+ return orig; |
+} |
+ |
+/* Unwrap symbol from UNSPEC_RELOC16 and, if unwrap_reloc32_p, |
+ UNSPEC_RELOC32 wrappers. */ |
+ |
+rtx |
+m68k_unwrap_symbol (rtx orig, bool unwrap_reloc32_p) |
+{ |
+ return m68k_unwrap_symbol_1 (orig, unwrap_reloc32_p, NULL); |
+} |
+ |
+/* Helper for m68k_final_prescan_insn. */ |
+ |
+static int |
+m68k_final_prescan_insn_1 (rtx *x_ptr, void *data ATTRIBUTE_UNUSED) |
+{ |
+ rtx x = *x_ptr; |
+ |
+ if (m68k_unwrap_symbol (x, true) != x) |
+ /* For rationale of the below, see comment in m68k_final_prescan_insn. */ |
+ { |
+ rtx plus; |
+ |
+ gcc_assert (GET_CODE (x) == CONST); |
+ plus = XEXP (x, 0); |
+ |
+ if (GET_CODE (plus) == PLUS || GET_CODE (plus) == MINUS) |
+ { |
+ rtx unspec; |
+ rtx addend; |
+ |
+ unspec = XEXP (plus, 0); |
+ gcc_assert (GET_CODE (unspec) == UNSPEC); |
+ addend = XEXP (plus, 1); |
+ gcc_assert (CONST_INT_P (addend)); |
+ |
+ /* We now have all the pieces, rearrange them. */ |
+ |
+ /* Move symbol to plus. */ |
+ XEXP (plus, 0) = XVECEXP (unspec, 0, 0); |
+ |
+ /* Move plus inside unspec. */ |
+ XVECEXP (unspec, 0, 0) = plus; |
+ |
+ /* Move unspec to top level of const. */ |
+ XEXP (x, 0) = unspec; |
+ } |
+ |
+ return -1; |
+ } |
+ |
+ return 0; |
+} |
+ |
+/* Prescan insn before outputing assembler for it. */ |
+ |
+void |
+m68k_final_prescan_insn (rtx insn ATTRIBUTE_UNUSED, |
+ rtx *operands, int n_operands) |
+{ |
+ int i; |
+ |
+ /* Combine and, possibly, other optimizations may do good job |
+ converting |
+ (const (unspec [(symbol)])) |
+ into |
+ (const (plus (unspec [(symbol)]) |
+ (const_int N))). |
+ The problem with this is emitting @TLS or @GOT decorations. |
+ The decoration is emitted when processing (unspec), so the |
+ result would be "#symbol@TLSLE+N" instead of "#symbol+N@TLSLE". |
+ |
+ It seems that the easiest solution to this is to convert such |
+ operands to |
+ (const (unspec [(plus (symbol) |
+ (const_int N))])). |
+ Note, that the top level of operand remains intact, so we don't have |
+ to patch up anything outside of the operand. */ |
+ |
+ for (i = 0; i < n_operands; ++i) |
+ { |
+ rtx op; |
+ |
+ op = operands[i]; |
+ |
+ for_each_rtx (&op, m68k_final_prescan_insn_1, NULL); |
+ } |
+} |
+ |
+/* Move X to a register and add REG_EQUAL note pointing to ORIG. |
+ If REG is non-null, use it; generate new pseudo otherwise. */ |
+ |
+static rtx |
+m68k_move_to_reg (rtx x, rtx orig, rtx reg) |
+{ |
+ rtx insn; |
+ |
+ if (reg == NULL_RTX) |
+ { |
+ gcc_assert (can_create_pseudo_p ()); |
+ reg = gen_reg_rtx (Pmode); |
+ } |
+ |
+ insn = emit_move_insn (reg, x); |
+ /* Put a REG_EQUAL note on this insn, so that it can be optimized |
+ by loop. */ |
+ set_unique_reg_note (insn, REG_EQUAL, orig); |
+ |
+ return reg; |
+} |
+ |
+/* Does the same as m68k_wrap_symbol, but returns a memory reference to |
+ GOT slot. */ |
+ |
+static rtx |
+m68k_wrap_symbol_into_got_ref (rtx x, enum m68k_reloc reloc, rtx temp_reg) |
+{ |
+ x = m68k_wrap_symbol (x, reloc, m68k_get_gp (), temp_reg); |
+ |
+ x = gen_rtx_MEM (Pmode, x); |
+ MEM_READONLY_P (x) = 1; |
+ |
+ return x; |
+} |
+ |
/* 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 |
@@ -2076,42 +2468,15 @@ legitimize_pic_address (rtx orig, enum machine_mode mode ATTRIBUTE_UNUSED, |
{ |
gcc_assert (reg); |
- if (TARGET_COLDFIRE && TARGET_XGOT) |
- /* When compiling with -mxgot switch the code for the above |
- example will look like this: |
- |
- 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; |
+ pic_ref = m68k_wrap_symbol_into_got_ref (orig, RELOC_GOT, reg); |
+ pic_ref = m68k_move_to_reg (pic_ref, orig, reg); |
} |
else if (GET_CODE (orig) == CONST) |
{ |
rtx base; |
/* Make sure this has not already been legitimized. */ |
- if (GET_CODE (XEXP (orig, 0)) == PLUS |
- && XEXP (XEXP (orig, 0), 0) == pic_offset_table_rtx) |
+ if (m68k_unwrap_symbol (orig, true) != orig) |
return orig; |
gcc_assert (reg); |
@@ -2124,13 +2489,257 @@ legitimize_pic_address (rtx orig, enum machine_mode mode ATTRIBUTE_UNUSED, |
base == reg ? 0 : reg); |
if (GET_CODE (orig) == CONST_INT) |
- return plus_constant (base, INTVAL (orig)); |
- pic_ref = gen_rtx_PLUS (Pmode, base, orig); |
- /* Likewise, should we set special REG_NOTEs here? */ |
+ pic_ref = plus_constant (base, INTVAL (orig)); |
+ else |
+ pic_ref = gen_rtx_PLUS (Pmode, base, orig); |
} |
+ |
return pic_ref; |
} |
+/* The __tls_get_addr symbol. */ |
+static GTY(()) rtx m68k_tls_get_addr; |
+ |
+/* Return SYMBOL_REF for __tls_get_addr. */ |
+ |
+static rtx |
+m68k_get_tls_get_addr (void) |
+{ |
+ if (m68k_tls_get_addr == NULL_RTX) |
+ m68k_tls_get_addr = init_one_libfunc ("__tls_get_addr"); |
+ |
+ return m68k_tls_get_addr; |
+} |
+ |
+/* Return libcall result in A0 instead of usual D0. */ |
+static bool m68k_libcall_value_in_a0_p = false; |
+ |
+/* Emit instruction sequence that calls __tls_get_addr. X is |
+ the TLS symbol we are referencing and RELOC is the symbol type to use |
+ (either TLSGD or TLSLDM). EQV is the REG_EQUAL note for the sequence |
+ emitted. A pseudo register with result of __tls_get_addr call is |
+ returned. */ |
+ |
+static rtx |
+m68k_call_tls_get_addr (rtx x, rtx eqv, enum m68k_reloc reloc) |
+{ |
+ rtx a0; |
+ rtx insns; |
+ rtx dest; |
+ |
+ /* Emit the call sequence. */ |
+ start_sequence (); |
+ |
+ /* FIXME: Unfortunately, emit_library_call_value does not |
+ consider (plus (%a5) (const (unspec))) to be a good enough |
+ operand for push, so it forces it into a register. The bad |
+ thing about this is that combiner, due to copy propagation and other |
+ optimizations, sometimes can not later fix this. As a consequence, |
+ additional register may be allocated resulting in a spill. |
+ For reference, see args processing loops in |
+ calls.c:emit_library_call_value_1. |
+ For testcase, see gcc.target/m68k/tls-{gd, ld}.c */ |
+ x = m68k_wrap_symbol (x, reloc, m68k_get_gp (), NULL_RTX); |
+ |
+ /* __tls_get_addr() is not a libcall, but emitting a libcall_value |
+ is the simpliest way of generating a call. The difference between |
+ __tls_get_addr() and libcall is that the result is returned in D0 |
+ instead of A0. To workaround this, we use m68k_libcall_value_in_a0_p |
+ which temporarily switches returning the result to A0. */ |
+ |
+ m68k_libcall_value_in_a0_p = true; |
+ a0 = emit_library_call_value (m68k_get_tls_get_addr (), NULL_RTX, LCT_PURE, |
+ Pmode, 1, x, Pmode); |
+ m68k_libcall_value_in_a0_p = false; |
+ |
+ insns = get_insns (); |
+ end_sequence (); |
+ |
+ gcc_assert (can_create_pseudo_p ()); |
+ dest = gen_reg_rtx (Pmode); |
+ emit_libcall_block (insns, dest, a0, eqv); |
+ |
+ return dest; |
+} |
+ |
+/* The __tls_get_addr symbol. */ |
+static GTY(()) rtx m68k_read_tp; |
+ |
+/* Return SYMBOL_REF for __m68k_read_tp. */ |
+ |
+static rtx |
+m68k_get_m68k_read_tp (void) |
+{ |
+ if (m68k_read_tp == NULL_RTX) |
+ m68k_read_tp = init_one_libfunc ("__m68k_read_tp"); |
+ |
+ return m68k_read_tp; |
+} |
+ |
+/* Emit instruction sequence that calls __m68k_read_tp. |
+ A pseudo register with result of __m68k_read_tp call is returned. */ |
+ |
+static rtx |
+m68k_call_m68k_read_tp (void) |
+{ |
+ rtx a0; |
+ rtx eqv; |
+ rtx insns; |
+ rtx dest; |
+ |
+ start_sequence (); |
+ |
+ /* __m68k_read_tp() is not a libcall, but emitting a libcall_value |
+ is the simpliest way of generating a call. The difference between |
+ __m68k_read_tp() and libcall is that the result is returned in D0 |
+ instead of A0. To workaround this, we use m68k_libcall_value_in_a0_p |
+ which temporarily switches returning the result to A0. */ |
+ |
+ /* Emit the call sequence. */ |
+ m68k_libcall_value_in_a0_p = true; |
+ a0 = emit_library_call_value (m68k_get_m68k_read_tp (), NULL_RTX, LCT_PURE, |
+ Pmode, 0); |
+ m68k_libcall_value_in_a0_p = false; |
+ insns = get_insns (); |
+ end_sequence (); |
+ |
+ /* Attach a unique REG_EQUIV, to allow the RTL optimizers to |
+ share the m68k_read_tp result with other IE/LE model accesses. */ |
+ eqv = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, const1_rtx), UNSPEC_RELOC32); |
+ |
+ gcc_assert (can_create_pseudo_p ()); |
+ dest = gen_reg_rtx (Pmode); |
+ emit_libcall_block (insns, dest, a0, eqv); |
+ |
+ return dest; |
+} |
+ |
+/* Return a legitimized address for accessing TLS SYMBOL_REF X. |
+ For explanations on instructions sequences see TLS/NPTL ABI for m68k and |
+ ColdFire. */ |
+ |
+rtx |
+m68k_legitimize_tls_address (rtx orig) |
+{ |
+ switch (SYMBOL_REF_TLS_MODEL (orig)) |
+ { |
+ case TLS_MODEL_GLOBAL_DYNAMIC: |
+ orig = m68k_call_tls_get_addr (orig, orig, RELOC_TLSGD); |
+ break; |
+ |
+ case TLS_MODEL_LOCAL_DYNAMIC: |
+ { |
+ rtx eqv; |
+ rtx a0; |
+ rtx x; |
+ |
+ /* Attach a unique REG_EQUIV, to allow the RTL optimizers to |
+ share the LDM result with other LD model accesses. */ |
+ eqv = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, const0_rtx), |
+ UNSPEC_RELOC32); |
+ |
+ a0 = m68k_call_tls_get_addr (orig, eqv, RELOC_TLSLDM); |
+ |
+ x = m68k_wrap_symbol (orig, RELOC_TLSLDO, a0, NULL_RTX); |
+ |
+ if (can_create_pseudo_p ()) |
+ x = m68k_move_to_reg (x, orig, NULL_RTX); |
+ |
+ orig = x; |
+ break; |
+ } |
+ |
+ case TLS_MODEL_INITIAL_EXEC: |
+ { |
+ rtx a0; |
+ rtx x; |
+ |
+ a0 = m68k_call_m68k_read_tp (); |
+ |
+ x = m68k_wrap_symbol_into_got_ref (orig, RELOC_TLSIE, NULL_RTX); |
+ x = gen_rtx_PLUS (Pmode, x, a0); |
+ |
+ if (can_create_pseudo_p ()) |
+ x = m68k_move_to_reg (x, orig, NULL_RTX); |
+ |
+ orig = x; |
+ break; |
+ } |
+ |
+ case TLS_MODEL_LOCAL_EXEC: |
+ { |
+ rtx a0; |
+ rtx x; |
+ |
+ a0 = m68k_call_m68k_read_tp (); |
+ |
+ x = m68k_wrap_symbol (orig, RELOC_TLSLE, a0, NULL_RTX); |
+ |
+ if (can_create_pseudo_p ()) |
+ x = m68k_move_to_reg (x, orig, NULL_RTX); |
+ |
+ orig = x; |
+ break; |
+ } |
+ |
+ default: |
+ gcc_unreachable (); |
+ } |
+ |
+ return orig; |
+} |
+ |
+/* Return true if X is a TLS symbol. */ |
+ |
+static bool |
+m68k_tls_symbol_p (rtx x) |
+{ |
+ if (!TARGET_HAVE_TLS) |
+ return false; |
+ |
+ if (GET_CODE (x) != SYMBOL_REF) |
+ return false; |
+ |
+ return SYMBOL_REF_TLS_MODEL (x) != 0; |
+} |
+ |
+/* Helper for m68k_tls_referenced_p. */ |
+ |
+static int |
+m68k_tls_reference_p_1 (rtx *x_ptr, void *data ATTRIBUTE_UNUSED) |
+{ |
+ /* Note: this is not the same as m68k_tls_symbol_p. */ |
+ if (GET_CODE (*x_ptr) == SYMBOL_REF) |
+ return SYMBOL_REF_TLS_MODEL (*x_ptr) != 0 ? 1 : 0; |
+ |
+ /* Don't recurse into legitimate TLS references. */ |
+ if (m68k_tls_reference_p (*x_ptr, true)) |
+ return -1; |
+ |
+ return 0; |
+} |
+ |
+/* If !LEGITIMATE_P, return true if X is a TLS symbol reference, |
+ though illegitimate one. |
+ If LEGITIMATE_P, return true if X is a legitimate TLS symbol reference. */ |
+ |
+bool |
+m68k_tls_reference_p (rtx x, bool legitimate_p) |
+{ |
+ if (!TARGET_HAVE_TLS) |
+ return false; |
+ |
+ if (!legitimate_p) |
+ return for_each_rtx (&x, m68k_tls_reference_p_1, NULL) == 1 ? true : false; |
+ else |
+ { |
+ enum m68k_reloc reloc = RELOC_GOT; |
+ |
+ return (m68k_unwrap_symbol_1 (x, true, &reloc) != x |
+ && TLS_RELOC_P (reloc)); |
+ } |
+} |
+ |
#define USE_MOVQ(i) ((unsigned) ((i) + 128) <= 255) |
@@ -2340,6 +2949,11 @@ m68k_rtx_costs (rtx x, int code, int outer_code, int *total, |
*total = COSTS_N_INSNS (43); /* div.l */ |
return true; |
+ case ZERO_EXTRACT: |
+ if (outer_code == COMPARE) |
+ *total = 0; |
+ return false; |
+ |
default: |
return false; |
} |
@@ -3918,20 +4532,146 @@ print_operand (FILE *file, rtx op, int letter) |
} |
} |
+/* Return string for TLS relocation RELOC. */ |
+ |
+static const char * |
+m68k_get_reloc_decoration (enum m68k_reloc reloc) |
+{ |
+ /* To my knowledge, !MOTOROLA assemblers don't support TLS. */ |
+ gcc_assert (MOTOROLA || reloc == RELOC_GOT); |
+ |
+ switch (reloc) |
+ { |
+ case RELOC_GOT: |
+ if (MOTOROLA) |
+ { |
+ if (flag_pic == 1 && TARGET_68020) |
+ return "@GOT.w"; |
+ else |
+ return "@GOT"; |
+ } |
+ else |
+ { |
+ if (TARGET_68020) |
+ { |
+ switch (flag_pic) |
+ { |
+ case 1: |
+ return ":w"; |
+ case 2: |
+ return ":l"; |
+ default: |
+ return ""; |
+ } |
+ } |
+ } |
+ |
+ case RELOC_TLSGD: |
+ return "@TLSGD"; |
+ |
+ case RELOC_TLSLDM: |
+ return "@TLSLDM"; |
+ |
+ case RELOC_TLSLDO: |
+ return "@TLSLDO"; |
+ |
+ case RELOC_TLSIE: |
+ return "@TLSIE"; |
+ |
+ case RELOC_TLSLE: |
+ return "@TLSLE"; |
+ |
+ default: |
+ gcc_unreachable (); |
+ } |
+} |
+ |
/* 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; |
+ if (GET_CODE (x) == UNSPEC) |
+ { |
+ switch (XINT (x, 1)) |
+ { |
+ 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); |
+ return true; |
- output_addr_const (file, XVECEXP (x, 0, 0)); |
- /* ??? What is the non-MOTOROLA syntax? */ |
- fputs ("@GOT", file); |
- return true; |
+ default: |
+ break; |
+ } |
+ } |
+ |
+ return false; |
+} |
+ |
+/* M68K implementation of TARGET_ASM_OUTPUT_DWARF_DTPREL. */ |
+ |
+static void |
+m68k_output_dwarf_dtprel (FILE *file, int size, rtx x) |
+{ |
+ gcc_assert (size == 4); |
+ fputs ("\t.long\t", file); |
+ output_addr_const (file, x); |
+ 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, y; |
+ rtx addend = NULL_RTX; |
+ rtx result; |
+ |
+ orig_x = delegitimize_mem_from_attrs (orig_x); |
+ if (! MEM_P (orig_x)) |
+ return orig_x; |
+ |
+ x = XEXP (orig_x, 0); |
+ |
+ if (GET_CODE (x) == PLUS |
+ && GET_CODE (XEXP (x, 1)) == CONST |
+ && REG_P (XEXP (x, 0)) |
+ && REGNO (XEXP (x, 0)) == PIC_REG) |
+ { |
+ y = x = XEXP (XEXP (x, 1), 0); |
+ |
+ /* Handle an addend. */ |
+ if ((GET_CODE (x) == PLUS || GET_CODE (x) == MINUS) |
+ && CONST_INT_P (XEXP (x, 1))) |
+ { |
+ addend = XEXP (x, 1); |
+ x = XEXP (x, 0); |
+ } |
+ |
+ if (GET_CODE (x) == UNSPEC |
+ && (XINT (x, 1) == UNSPEC_RELOC16 |
+ || XINT (x, 1) == UNSPEC_RELOC32)) |
+ { |
+ result = XVECEXP (x, 0, 0); |
+ if (addend) |
+ { |
+ if (GET_CODE (y) == PLUS) |
+ result = gen_rtx_PLUS (Pmode, result, addend); |
+ else |
+ result = gen_rtx_MINUS (Pmode, result, addend); |
+ result = gen_rtx_CONST (Pmode, result); |
+ } |
+ return result; |
+ } |
+ } |
+ |
+ return orig_x; |
+} |
+ |
/* A C compound statement to output to stdio stream STREAM the |
assembler syntax for an instruction operand that is a memory |
@@ -4019,15 +4759,8 @@ print_operand_address (FILE *file, rtx addr) |
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"); |
- } |
- } |
+ output_addr_const (file, address.offset); |
+ |
putc ('(', file); |
if (address.base) |
fputs (M68K_REGNAME (REGNO (address.base)), file); |
@@ -4060,19 +4793,7 @@ print_operand_address (FILE *file, rtx addr) |
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; |
- } |
- } |
+ output_addr_const (file, address.offset); |
} |
/* Print the ",index" component, if any. */ |
if (address.index) |
@@ -4188,7 +4909,7 @@ strict_low_part_peephole_ok (enum machine_mode mode, rtx first_insn, |
simple fact that the m68k does not allow a pc-relative addressing |
mode as a destination. gcc does not distinguish between source and |
destination addresses. Hence, if we claim that pc-relative address |
- modes are valid, e.g. GO_IF_LEGITIMATE_ADDRESS accepts them, then we |
+ modes are valid, e.g. TARGET_LEGITIMATE_ADDRESS_P accepts them, then we |
end up with invalid code. To get around this problem, we left |
pc-relative modes as invalid addresses, and then added special |
predicates and constraints to accept them. |
@@ -4224,7 +4945,7 @@ output_andsi3 (rtx *operands) |
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]))) |
{ |
@@ -4261,7 +4982,7 @@ output_iorsi3 (rtx *operands) |
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]))) |
{ |
@@ -4296,7 +5017,7 @@ output_xorsi3 (rtx *operands) |
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]))) |
{ |
@@ -4336,32 +5057,16 @@ output_sibcall (rtx x) |
return "jmp %a0"; |
} |
-#ifdef M68K_TARGET_COFF |
- |
-/* Output assembly to switch to section NAME with attribute FLAGS. */ |
- |
-static void |
-m68k_coff_asm_named_section (const char *name, unsigned int flags, |
- tree decl ATTRIBUTE_UNUSED) |
-{ |
- char flagchar; |
- |
- if (flags & SECTION_WRITE) |
- flagchar = 'd'; |
- else |
- flagchar = 'x'; |
- |
- fprintf (asm_out_file, "\t.section\t%s,\"%c\"\n", name, flagchar); |
-} |
- |
-#endif /* M68K_TARGET_COFF */ |
- |
static void |
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; |
+ 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; |
@@ -4388,15 +5093,15 @@ m68k_output_mi_thunk (FILE *file, tree thunk ATTRIBUTE_UNUSED, |
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 (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. */ |
@@ -4422,8 +5127,8 @@ m68k_output_mi_thunk (FILE *file, tree thunk ATTRIBUTE_UNUSED, |
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; |
@@ -4441,7 +5146,6 @@ m68k_output_mi_thunk (FILE *file, tree thunk ATTRIBUTE_UNUSED, |
/* 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. */ |
@@ -4580,9 +5284,13 @@ m68k_libcall_value (enum machine_mode mode) |
default: |
break; |
} |
- return gen_rtx_REG (mode, D0_REG); |
+ |
+ 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) |
{ |
@@ -4846,9 +5554,8 @@ sched_attr_op_type (rtx insn, bool opx_p, bool address_p) |
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. */ |
+ if (symbolic_operand (m68k_unwrap_symbol (op, false), VOIDmode)) |
+ /* Just a guess. */ |
return OP_TYPE_IMM_W; |
return OP_TYPE_IMM_L; |
@@ -5793,3 +6500,31 @@ m68k_sched_indexed_address_bypass_p (rtx pro, rtx con) |
return 0; |
} |
} |
+ |
+/* 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)); |
+} |
+ |
+#include "gt-m68k.h" |