/* Subroutines for code generation on Motorola 68HC11 and 68HC12.
- Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007
- Free Software Foundation, Inc.
+ Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008,
+ 2009, 2010 Free Software Foundation, Inc.
Contributed by Stephane Carrez (stcarrez@nerim.fr)
This file is part of GCC.
#include "tm.h"
#include "rtl.h"
#include "tree.h"
+#include "expr.h"
#include "tm_p.h"
#include "regs.h"
#include "hard-reg-set.h"
-#include "real.h"
#include "insn-config.h"
#include "conditions.h"
#include "output.h"
#include "recog.h"
#include "expr.h"
#include "libfuncs.h"
+#include "diagnostic-core.h"
#include "toplev.h"
#include "basic-block.h"
#include "function.h"
#include "target-def.h"
#include "df.h"
+static void m68hc11_option_override (void);
static void emit_move_after_reload (rtx, rtx, rtx);
static rtx simplify_logical (enum machine_mode, int, rtx, rtx *);
-static void m68hc11_emit_logical (enum machine_mode, int, rtx *);
+static void m68hc11_emit_logical (enum machine_mode, enum rtx_code, rtx *);
static void m68hc11_reorg (void);
-static int go_if_legitimate_address_internal (rtx, enum machine_mode, int);
+static bool m68hc11_legitimate_address_p_1 (enum machine_mode, rtx, bool);
+static bool m68hc11_legitimate_address_p (enum machine_mode, rtx, bool);
static rtx m68hc11_expand_compare (enum rtx_code, rtx, rtx);
static int must_parenthesize (rtx);
static int m68hc11_address_cost (rtx, bool);
static int m68hc11_rtx_costs_1 (rtx, enum rtx_code, enum rtx_code);
static bool m68hc11_rtx_costs (rtx, int, int, int *, bool);
static tree m68hc11_handle_fntype_attribute (tree *, tree, tree, int, bool *);
-const struct attribute_spec m68hc11_attribute_table[];
+static tree m68hc11_handle_page0_attribute (tree *, tree, tree, int, bool *);
+static bool m68hc11_class_likely_spilled_p (reg_class_t);
void create_regs_rtx (void);
static void asm_print_register (FILE *, int);
+static void m68hc11_print_operand (FILE *, rtx, int);
+static void m68hc11_print_operand_address (FILE *, rtx);
static void m68hc11_output_function_epilogue (FILE *, HOST_WIDE_INT);
static void m68hc11_asm_out_constructor (rtx, int);
static void m68hc11_asm_out_destructor (rtx, int);
static void m68hc11_init_libfuncs (void);
static rtx m68hc11_struct_value_rtx (tree, int);
static bool m68hc11_return_in_memory (const_tree, const_tree);
+static bool m68hc11_can_eliminate (const int, const int);
+static void m68hc11_trampoline_init (rtx, tree, rtx);
/* Must be set to 1 to produce debug messages. */
int debug_m6811 = 0;
int m68hc11_addr_mode;
int m68hc11_mov_addr_mode;
-
-/* Comparison operands saved by the "tstxx" and "cmpxx" expand patterns. */
-rtx m68hc11_compare_op0;
-rtx m68hc11_compare_op1;
\f
const struct processor_costs *m68hc11_cost;
COSTS_N_INSNS (100)
};
\f
+/* M68HC11 specific attributes. */
+
+static const struct attribute_spec m68hc11_attribute_table[] =
+{
+ /* { name, min_len, max_len, decl_req, type_req, fn_type_req, handler } */
+ { "interrupt", 0, 0, false, true, true, m68hc11_handle_fntype_attribute },
+ { "trap", 0, 0, false, true, true, m68hc11_handle_fntype_attribute },
+ { "far", 0, 0, false, true, true, m68hc11_handle_fntype_attribute },
+ { "near", 0, 0, false, true, true, m68hc11_handle_fntype_attribute },
+ { "page0", 0, 0, false, false, false, m68hc11_handle_page0_attribute },
+ { NULL, 0, 0, false, false, false, NULL }
+};
+\f
/* Initialize the GCC target structure. */
#undef TARGET_ATTRIBUTE_TABLE
#define TARGET_ATTRIBUTE_TABLE m68hc11_attribute_table
#undef TARGET_ASM_ALIGNED_HI_OP
#define TARGET_ASM_ALIGNED_HI_OP "\t.word\t"
+#undef TARGET_PRINT_OPERAND
+#define TARGET_PRINT_OPERAND m68hc11_print_operand
+#undef TARGET_PRINT_OPERAND_ADDRESS
+#define TARGET_PRINT_OPERAND_ADDRESS m68hc11_print_operand_address
+
#undef TARGET_ASM_FUNCTION_EPILOGUE
#define TARGET_ASM_FUNCTION_EPILOGUE m68hc11_output_function_epilogue
#undef TARGET_STRIP_NAME_ENCODING
#define TARGET_STRIP_NAME_ENCODING m68hc11_strip_name_encoding
+#undef TARGET_LEGITIMATE_ADDRESS_P
+#define TARGET_LEGITIMATE_ADDRESS_P m68hc11_legitimate_address_p
+
+#undef TARGET_CAN_ELIMINATE
+#define TARGET_CAN_ELIMINATE m68hc11_can_eliminate
+
+#undef TARGET_CLASS_LIKELY_SPILLED_P
+#define TARGET_CLASS_LIKELY_SPILLED_P m68hc11_class_likely_spilled_p
+
+#undef TARGET_TRAMPOLINE_INIT
+#define TARGET_TRAMPOLINE_INIT m68hc11_trampoline_init
+
+#undef TARGET_OPTION_OVERRIDE
+#define TARGET_OPTION_OVERRIDE m68hc11_option_override
+
struct gcc_target targetm = TARGET_INITIALIZER;
\f
-int
-m68hc11_override_options (void)
+static void
+m68hc11_option_override (void)
{
memset (m68hc11_reg_valid_for_index, 0,
sizeof (m68hc11_reg_valid_for_index));
if (TARGET_LONG_CALLS)
current_function_far = 1;
}
- return 0;
}
return rclass;
}
+/* Implement TARGET_CLASS_LIKELY_SPILLED_P. */
+
+static bool
+m68hc11_class_likely_spilled_p (reg_class_t rclass)
+{
+ switch (rclass)
+ {
+ case D_REGS:
+ case X_REGS:
+ case Y_REGS:
+ case A_REGS:
+ case SP_REGS:
+ case D_OR_X_REGS:
+ case D_OR_Y_REGS:
+ case X_OR_SP_REGS:
+ case Y_OR_SP_REGS:
+ case D_OR_SP_REGS:
+ return true;
+
+ default:
+ break;
+ }
+
+ return false;
+}
+
/* Return 1 if the operand is a valid indexed addressing mode.
For 68hc11: n,r with n in [0..255] and r in A_REGS class
For 68hc12: n,r no constraint on the constant, r in A_REGS class. */
&& reg_equiv_memory_loc[REGNO (operand)])
{
operand = reg_equiv_memory_loc[REGNO (operand)];
- operand = eliminate_regs (operand, 0, NULL_RTX);
+ operand = eliminate_regs (operand, VOIDmode, NULL_RTX);
}
if (GET_CODE (operand) != MEM)
&& reg_equiv_memory_loc[REGNO (operand)])
{
operand = reg_equiv_memory_loc[REGNO (operand)];
- operand = eliminate_regs (operand, 0, NULL_RTX);
+ operand = eliminate_regs (operand, VOIDmode, NULL_RTX);
}
if (GET_CODE (operand) != MEM)
return 0;
return m68hc11_valid_addressing_p (operand, mode, addr_mode);
}
-static int
-go_if_legitimate_address_internal (rtx operand, enum machine_mode mode,
- int strict)
+static bool
+m68hc11_legitimate_address_p_1 (enum machine_mode mode, rtx operand,
+ bool strict)
{
int addr_mode;
return 0;
}
-int
-m68hc11_go_if_legitimate_address (rtx operand, enum machine_mode mode,
- int strict)
+bool
+m68hc11_legitimate_address_p (enum machine_mode mode, rtx operand,
+ bool strict)
{
int result;
debug_rtx (operand);
}
- result = go_if_legitimate_address_internal (operand, mode, strict);
+ result = m68hc11_legitimate_address_p_1 (mode, operand, strict);
if (debug_m6811)
{
return result;
}
-int
-m68hc11_legitimize_address (rtx *operand ATTRIBUTE_UNUSED,
- rtx old_operand ATTRIBUTE_UNUSED,
- enum machine_mode mode ATTRIBUTE_UNUSED)
-{
- return 0;
-}
-
int
m68hc11_reload_operands (rtx operands[])
jmp FNADDR
*/
-void
-m68hc11_initialize_trampoline (rtx tramp, rtx fnaddr, rtx cxt)
+static void
+m68hc11_trampoline_init (rtx m_tramp, tree fndecl, rtx cxt)
{
const char *static_chain_reg = reg_names[STATIC_CHAIN_REGNUM];
+ rtx fnaddr = XEXP (DECL_RTL (fndecl), 0);
+ rtx mem;
/* Skip the '*'. */
if (*static_chain_reg == '*')
static_chain_reg++;
if (TARGET_M6811)
{
- emit_move_insn (gen_rtx_MEM (HImode, tramp), GEN_INT (0x18ce));
- emit_move_insn (gen_rtx_MEM (HImode, plus_constant (tramp, 2)), cxt);
- emit_move_insn (gen_rtx_MEM (HImode, plus_constant (tramp, 4)),
- GEN_INT (0x18df));
- emit_move_insn (gen_rtx_MEM (QImode, plus_constant (tramp, 6)),
+ mem = adjust_address (m_tramp, HImode, 0);
+ emit_move_insn (mem, GEN_INT (0x18ce));
+ mem = adjust_address (m_tramp, HImode, 2);
+ emit_move_insn (mem, cxt);
+ mem = adjust_address (m_tramp, HImode, 4);
+ emit_move_insn (mem, GEN_INT (0x18df));
+ mem = adjust_address (m_tramp, QImode, 6);
+ emit_move_insn (mem,
gen_rtx_CONST (QImode,
gen_rtx_SYMBOL_REF (Pmode,
static_chain_reg)));
- emit_move_insn (gen_rtx_MEM (QImode, plus_constant (tramp, 7)),
- GEN_INT (0x7e));
- emit_move_insn (gen_rtx_MEM (HImode, plus_constant (tramp, 8)), fnaddr);
+ mem = adjust_address (m_tramp, QImode, 7);
+ emit_move_insn (mem, GEN_INT (0x7e));
+ mem = adjust_address (m_tramp, HImode, 8);
+ emit_move_insn (mem, fnaddr);
}
else
{
- emit_move_insn (gen_rtx_MEM (HImode, tramp), GEN_INT (0x1803));
- emit_move_insn (gen_rtx_MEM (HImode, plus_constant (tramp, 2)), cxt);
- emit_move_insn (gen_rtx_MEM (HImode, plus_constant (tramp, 4)),
+ mem = adjust_address (m_tramp, HImode, 0);
+ emit_move_insn (mem, GEN_INT (0x1803));
+ mem = adjust_address (m_tramp, HImode, 2);
+ emit_move_insn (mem, cxt);
+ mem = adjust_address (m_tramp, HImode, 4);
+ emit_move_insn (mem,
gen_rtx_CONST (HImode,
gen_rtx_SYMBOL_REF (Pmode,
static_chain_reg)));
- emit_move_insn (gen_rtx_MEM (QImode, plus_constant (tramp, 6)),
- GEN_INT (0x06));
- emit_move_insn (gen_rtx_MEM (HImode, plus_constant (tramp, 7)), fnaddr);
+ mem = adjust_address (m_tramp, QImode, 6);
+ emit_move_insn (mem, GEN_INT (0x06));
+ mem = adjust_address (m_tramp, HImode, 7);
+ emit_move_insn (mem, fnaddr);
}
}
\f
}
else
{
- warning (OPT_Wattributes, "%qs attribute ignored",
- IDENTIFIER_POINTER (name));
+ warning (OPT_Wattributes, "%qE attribute ignored",
+ name);
*no_add_attrs = true;
}
return NULL_TREE;
}
-const struct attribute_spec m68hc11_attribute_table[] =
-{
- /* { name, min_len, max_len, decl_req, type_req, fn_type_req, handler } */
- { "interrupt", 0, 0, false, true, true, m68hc11_handle_fntype_attribute },
- { "trap", 0, 0, false, true, true, m68hc11_handle_fntype_attribute },
- { "far", 0, 0, false, true, true, m68hc11_handle_fntype_attribute },
- { "near", 0, 0, false, true, true, m68hc11_handle_fntype_attribute },
- { "page0", 0, 0, false, false, false, m68hc11_handle_page0_attribute },
- { NULL, 0, 0, false, false, false, NULL }
-};
-
/* Keep track of the symbol which has a `trap' attribute and which uses
the `swi' calling convention. Since there is only one trap, we only
record one such symbol. If there are several, a warning is reported. */
&& TREE_CODE (*node) != FIELD_DECL
&& TREE_CODE (*node) != TYPE_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;
}
/* Argument support functions. */
+/* Given FROM and TO register numbers, say whether this elimination is
+ allowed. Frame pointer elimination is automatically handled.
+
+ All other eliminations are valid. */
+
+bool
+m68hc11_can_eliminate (const int from, const int to)
+{
+ return (from == ARG_POINTER_REGNUM && to == STACK_POINTER_REGNUM
+ ? ! frame_pointer_needed
+ : true);
+}
+
/* Define the offset between two registers, one to be eliminated, and the
other its replacement, at the start of a routine. */
int
`downward' to pad below, or `none' to inhibit padding.
Structures are stored left shifted in their argument slot. */
-int
+enum direction
m68hc11_function_arg_padding (enum machine_mode mode, const_tree type)
{
if (type != 0 && AGGREGATE_TYPE_P (type))
/* Put a REG_INC note to tell the flow analysis that the instruction
is necessary. */
if (IS_STACK_PUSH (to))
- {
- REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_INC,
- XEXP (XEXP (to, 0), 0),
- REG_NOTES (insn));
- }
+ add_reg_note (insn, REG_INC, XEXP (XEXP (to, 0), 0));
else if (IS_STACK_POP (from))
- {
- REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_INC,
- XEXP (XEXP (from, 0), 0),
- REG_NOTES (insn));
- }
+ add_reg_note (insn, REG_INC, XEXP (XEXP (from, 0), 0));
/* For 68HC11, put a REG_INC note on `sts _.frame' to prevent the cse-reg
to think that sp == _.frame and later replace a x = sp with x = _.frame.
The problem is that we are lying to gcc and use `txs' for x = sp
(which is not really true because txs is really x = sp + 1). */
else if (TARGET_M6811 && SP_REG_P (from))
- {
- REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_INC,
- from,
- REG_NOTES (insn));
- }
+ add_reg_note (insn, REG_INC, from);
}
int
'T' generate the low-part temporary scratch register. The operand is
ignored. */
-void
-print_operand (FILE *file, rtx op, int letter)
+static void
+m68hc11_print_operand (FILE *file, rtx op, int letter)
{
if (letter == 't')
{
case MEM:
gcc_assert (TARGET_M6812);
fprintf (file, "[");
- print_operand_address (file, XEXP (base, 0));
+ m68hc11_print_operand_address (file, XEXP (base, 0));
fprintf (file, "]");
break;
assembler syntax for an instruction operand that is a memory
reference whose address is ADDR. ADDR is an RTL expression. */
-void
-print_operand_address (FILE *file, rtx addr)
+static void
+m68hc11_print_operand_address (FILE *file, rtx addr)
{
rtx base;
rtx offset;
}
static void
-m68hc11_emit_logical (enum machine_mode mode, int code, rtx *operands)
+m68hc11_emit_logical (enum machine_mode mode, enum rtx_code code, rtx *operands)
{
rtx result;
int need_copy;
}
void
-m68hc11_split_logical (enum machine_mode mode, int code, rtx *operands)
+m68hc11_split_logical (enum machine_mode mode, enum rtx_code code,
+ rtx *operands)
{
rtx low[4];
rtx high[4];
{
cc_status.flags = 0;
cc_status.value1 = XEXP (exp, 0);
- cc_status.value2 = XEXP (exp, 1);
+ if (GET_CODE (XEXP (exp, 1)) == COMPARE
+ && XEXP (XEXP (exp, 1), 1) == CONST0_RTX (GET_MODE (XEXP (XEXP (exp, 1), 0))))
+ cc_status.value2 = XEXP (XEXP (exp, 1), 0);
+ else
+ cc_status.value2 = XEXP (exp, 1);
}
else
{
case COMPARE:
case ABS:
case ZERO_EXTEND:
+ case ZERO_EXTRACT:
total = extra_cost + rtx_cost (XEXP (x, 0), code, !optimize_size);
if (mode == QImode)
{
}
static bool
-m68hc11_rtx_costs (rtx x, int code, int outer_code, int *total,
+m68hc11_rtx_costs (rtx x, int codearg, int outer_code_arg, int *total,
bool speed ATTRIBUTE_UNUSED)
{
+ enum rtx_code code = (enum rtx_code) codearg;
+ enum rtx_code outer_code = (enum rtx_code) outer_code_arg;
+
switch (code)
{
/* Constants are cheap. Moving them in registers must be avoided
*total = 0;
return true;
+ case ZERO_EXTRACT:
+ if (outer_code != COMPARE)
+ return false;
+
case ROTATE:
case ROTATERT:
case ASHIFT: