/* Subroutines for insn-output.c for Tensilica's Xtensa architecture.
- Copyright 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009
+ Copyright 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011
Free Software Foundation, Inc.
Contributed by Bob Wilson (bwilson@tensilica.com) at Tensilica.
#include "reload.h"
#include "tm_p.h"
#include "function.h"
-#include "toplev.h"
+#include "diagnostic-core.h"
#include "optabs.h"
#include "libfuncs.h"
#include "ggc.h"
ACC_REG,
};
+static void xtensa_option_override (void);
static enum internal_test map_test_to_internal_test (enum rtx_code);
static rtx gen_int_relational (enum rtx_code, rtx, rtx, int *);
static rtx gen_float_relational (enum rtx_code, rtx, rtx);
static struct machine_function * xtensa_init_machine_status (void);
static rtx xtensa_legitimize_tls_address (rtx);
static rtx xtensa_legitimize_address (rtx, rtx, enum machine_mode);
+static bool xtensa_mode_dependent_address_p (const_rtx);
static bool xtensa_return_in_msb (const_tree);
static void printx (FILE *, signed int);
static void xtensa_function_epilogue (FILE *, HOST_WIDE_INT);
int) ATTRIBUTE_UNUSED;
static section *xtensa_select_rtx_section (enum machine_mode, rtx,
unsigned HOST_WIDE_INT);
-static bool xtensa_rtx_costs (rtx, int, int, int *, bool);
+static bool xtensa_rtx_costs (rtx, int, int, int, int *, bool);
+static int xtensa_register_move_cost (enum machine_mode, reg_class_t,
+ reg_class_t);
+static int xtensa_memory_move_cost (enum machine_mode, reg_class_t, bool);
static tree xtensa_build_builtin_va_list (void);
static bool xtensa_return_in_memory (const_tree, const_tree);
static tree xtensa_gimplify_va_arg_expr (tree, tree, gimple_seq *,
gimple_seq *);
+static void xtensa_function_arg_advance (cumulative_args_t, enum machine_mode,
+ const_tree, bool);
+static rtx xtensa_function_arg (cumulative_args_t, enum machine_mode,
+ const_tree, bool);
+static rtx xtensa_function_incoming_arg (cumulative_args_t,
+ enum machine_mode, const_tree, bool);
static rtx xtensa_function_value (const_tree, const_tree, bool);
+static rtx xtensa_libcall_value (enum machine_mode, const_rtx);
+static bool xtensa_function_value_regno_p (const unsigned int);
+static unsigned int xtensa_function_arg_boundary (enum machine_mode,
+ const_tree);
static void xtensa_init_builtins (void);
static tree xtensa_fold_builtin (tree, int, tree *, bool);
static rtx xtensa_expand_builtin (tree, rtx, rtx, enum machine_mode, int);
static rtx xtensa_static_chain (const_tree, bool);
static void xtensa_asm_trampoline_template (FILE *);
static void xtensa_trampoline_init (rtx, tree, rtx);
+static bool xtensa_output_addr_const_extra (FILE *, rtx);
+static bool xtensa_cannot_force_const_mem (enum machine_mode, rtx);
+
+static reg_class_t xtensa_preferred_reload_class (rtx, reg_class_t);
+static reg_class_t xtensa_preferred_output_reload_class (rtx, reg_class_t);
+static reg_class_t xtensa_secondary_reload (bool, rtx, reg_class_t,
+ enum machine_mode,
+ struct secondary_reload_info *);
+
+static bool constantpool_address_p (const_rtx addr);
+static bool xtensa_legitimate_constant_p (enum machine_mode, rtx);
+
+static bool xtensa_member_type_forces_blk (const_tree,
+ enum machine_mode mode);
static const int reg_nonleaf_alloc_order[FIRST_PSEUDO_REGISTER] =
REG_ALLOC_ORDER;
#undef TARGET_ASM_SELECT_RTX_SECTION
#define TARGET_ASM_SELECT_RTX_SECTION xtensa_select_rtx_section
-#undef TARGET_DEFAULT_TARGET_FLAGS
-#define TARGET_DEFAULT_TARGET_FLAGS (TARGET_DEFAULT | MASK_FUSED_MADD)
-
#undef TARGET_LEGITIMIZE_ADDRESS
#define TARGET_LEGITIMIZE_ADDRESS xtensa_legitimize_address
+#undef TARGET_MODE_DEPENDENT_ADDRESS_P
+#define TARGET_MODE_DEPENDENT_ADDRESS_P xtensa_mode_dependent_address_p
+#undef TARGET_REGISTER_MOVE_COST
+#define TARGET_REGISTER_MOVE_COST xtensa_register_move_cost
+#undef TARGET_MEMORY_MOVE_COST
+#define TARGET_MEMORY_MOVE_COST xtensa_memory_move_cost
#undef TARGET_RTX_COSTS
#define TARGET_RTX_COSTS xtensa_rtx_costs
#undef TARGET_ADDRESS_COST
-#define TARGET_ADDRESS_COST hook_int_rtx_bool_0
+#define TARGET_ADDRESS_COST hook_int_rtx_mode_as_bool_0
+
+#undef TARGET_MEMBER_TYPE_FORCES_BLK
+#define TARGET_MEMBER_TYPE_FORCES_BLK xtensa_member_type_forces_blk
#undef TARGET_BUILD_BUILTIN_VA_LIST
#define TARGET_BUILD_BUILTIN_VA_LIST xtensa_build_builtin_va_list
#define TARGET_RETURN_IN_MEMORY xtensa_return_in_memory
#undef TARGET_FUNCTION_VALUE
#define TARGET_FUNCTION_VALUE xtensa_function_value
+#undef TARGET_LIBCALL_VALUE
+#define TARGET_LIBCALL_VALUE xtensa_libcall_value
+#undef TARGET_FUNCTION_VALUE_REGNO_P
+#define TARGET_FUNCTION_VALUE_REGNO_P xtensa_function_value_regno_p
+
#undef TARGET_SPLIT_COMPLEX_ARG
#define TARGET_SPLIT_COMPLEX_ARG hook_bool_const_tree_true
#undef TARGET_MUST_PASS_IN_STACK
#define TARGET_MUST_PASS_IN_STACK must_pass_in_stack_var_size
+#undef TARGET_FUNCTION_ARG_ADVANCE
+#define TARGET_FUNCTION_ARG_ADVANCE xtensa_function_arg_advance
+#undef TARGET_FUNCTION_ARG
+#define TARGET_FUNCTION_ARG xtensa_function_arg
+#undef TARGET_FUNCTION_INCOMING_ARG
+#define TARGET_FUNCTION_INCOMING_ARG xtensa_function_incoming_arg
+#undef TARGET_FUNCTION_ARG_BOUNDARY
+#define TARGET_FUNCTION_ARG_BOUNDARY xtensa_function_arg_boundary
#undef TARGET_EXPAND_BUILTIN_SAVEREGS
#define TARGET_EXPAND_BUILTIN_SAVEREGS xtensa_builtin_saveregs
#undef TARGET_EXPAND_BUILTIN
#define TARGET_EXPAND_BUILTIN xtensa_expand_builtin
+#undef TARGET_PREFERRED_RELOAD_CLASS
+#define TARGET_PREFERRED_RELOAD_CLASS xtensa_preferred_reload_class
+#undef TARGET_PREFERRED_OUTPUT_RELOAD_CLASS
+#define TARGET_PREFERRED_OUTPUT_RELOAD_CLASS xtensa_preferred_output_reload_class
+
#undef TARGET_SECONDARY_RELOAD
#define TARGET_SECONDARY_RELOAD xtensa_secondary_reload
#define TARGET_HAVE_TLS (TARGET_THREADPTR && HAVE_AS_TLS)
#undef TARGET_CANNOT_FORCE_CONST_MEM
-#define TARGET_CANNOT_FORCE_CONST_MEM xtensa_tls_referenced_p
+#define TARGET_CANNOT_FORCE_CONST_MEM xtensa_cannot_force_const_mem
#undef TARGET_LEGITIMATE_ADDRESS_P
#define TARGET_LEGITIMATE_ADDRESS_P xtensa_legitimate_address_p
#undef TARGET_TRAMPOLINE_INIT
#define TARGET_TRAMPOLINE_INIT xtensa_trampoline_init
+#undef TARGET_OPTION_OVERRIDE
+#define TARGET_OPTION_OVERRIDE xtensa_option_override
+
+#undef TARGET_ASM_OUTPUT_ADDR_CONST_EXTRA
+#define TARGET_ASM_OUTPUT_ADDR_CONST_EXTRA xtensa_output_addr_const_extra
+
+#undef TARGET_LEGITIMATE_CONSTANT_P
+#define TARGET_LEGITIMATE_CONSTANT_P xtensa_legitimate_constant_p
+
struct gcc_target targetm = TARGET_INITIALIZER;
\f
}
-int
-constantpool_address_p (rtx addr)
+static bool
+constantpool_address_p (const_rtx addr)
{
- rtx sym = addr;
+ const_rtx sym = addr;
if (GET_CODE (addr) == CONST)
{
/* Only handle (PLUS (SYM, OFFSET)) form. */
addr = XEXP (addr, 0);
if (GET_CODE (addr) != PLUS)
- return FALSE;
+ return false;
/* Make sure the address is word aligned. */
offset = XEXP (addr, 1);
- if ((GET_CODE (offset) != CONST_INT)
+ if ((!CONST_INT_P (offset))
|| ((INTVAL (offset) & 3) != 0))
- return FALSE;
+ return false;
sym = XEXP (addr, 0);
}
if ((GET_CODE (sym) == SYMBOL_REF)
&& CONSTANT_POOL_ADDRESS_P (sym))
- return TRUE;
- return FALSE;
+ return true;
+ return false;
}
{
rtx temp =
gen_rtx_SUBREG (GET_MODE (x),
- reg_equiv_mem [REGNO (SUBREG_REG (x))],
+ reg_equiv_mem (REGNO (SUBREG_REG (x))),
SUBREG_BYTE (x));
x = alter_subreg (&temp);
}
containing_fp = force_reg (Pmode, containing_fp);
emit_library_call (gen_rtx_SYMBOL_REF (Pmode, "__xtensa_nonlocal_goto"),
- 0, VOIDmode, 2,
+ LCT_NORMAL, VOIDmode, 2,
containing_fp, Pmode,
goto_handler, Pmode);
}
emit_library_call
(gen_rtx_SYMBOL_REF (Pmode, "__xtensa_libgcc_window_spill"),
- 0, VOIDmode, 0);
+ LCT_NORMAL, VOIDmode, 0);
}
emit_insn (gen_tls_arg (arg, sym));
emit_move_insn (a10, arg);
call_insn = emit_call_insn (gen_tls_call (a10, fn, sym, const1_rtx));
- CALL_INSN_FUNCTION_USAGE (call_insn)
- = gen_rtx_EXPR_LIST (VOIDmode, gen_rtx_USE (VOIDmode, a10),
- CALL_INSN_FUNCTION_USAGE (call_insn));
+ use_reg (&CALL_INSN_FUNCTION_USAGE (call_insn), a10);
insns = get_insns ();
end_sequence ();
return x;
}
+/* Worker function for TARGET_MODE_DEPENDENT_ADDRESS_P.
+
+ Treat constant-pool references as "mode dependent" since they can
+ only be accessed with SImode loads. This works around a bug in the
+ combiner where a constant pool reference is temporarily converted
+ to an HImode load, which is then assumed to zero-extend based on
+ our definition of LOAD_EXTEND_OP. This is wrong because the high
+ bits of a 16-bit value in the constant pool are now sign-extended
+ by default. */
+
+static bool
+xtensa_mode_dependent_address_p (const_rtx addr)
+{
+ return constantpool_address_p (addr);
+}
/* Helper for xtensa_tls_referenced_p. */
}
+/* Implement TARGET_CANNOT_FORCE_CONST_MEM. */
+
+static bool
+xtensa_cannot_force_const_mem (enum machine_mode mode ATTRIBUTE_UNUSED, rtx x)
+{
+ return xtensa_tls_referenced_p (x);
+}
+
+
/* Return the debugger register number to use for 'regno'. */
int
/* Advance the argument to the next argument position. */
-void
-function_arg_advance (CUMULATIVE_ARGS *cum, enum machine_mode mode, tree type)
+static void
+xtensa_function_arg_advance (cumulative_args_t cum, enum machine_mode mode,
+ const_tree type, bool named ATTRIBUTE_UNUSED)
{
int words, max;
int *arg_words;
- arg_words = &cum->arg_words;
+ arg_words = &get_cumulative_args (cum)->arg_words;
max = MAX_ARGS_IN_REGISTERS;
words = (((mode != BLKmode)
or 0 if the argument is to be passed on the stack. INCOMING_P is nonzero
if this is an incoming argument to the current function. */
-rtx
-function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode, tree type,
- int incoming_p)
+static rtx
+xtensa_function_arg_1 (cumulative_args_t cum_v, enum machine_mode mode,
+ const_tree type, bool incoming_p)
{
+ CUMULATIVE_ARGS *cum = get_cumulative_args (cum_v);
int regbase, words, max;
int *arg_words;
int regno;
return gen_rtx_REG (mode, regno);
}
+/* Implement TARGET_FUNCTION_ARG. */
-int
-function_arg_boundary (enum machine_mode mode, tree type)
+static rtx
+xtensa_function_arg (cumulative_args_t cum, enum machine_mode mode,
+ const_tree type, bool named ATTRIBUTE_UNUSED)
+{
+ return xtensa_function_arg_1 (cum, mode, type, false);
+}
+
+/* Implement TARGET_FUNCTION_INCOMING_ARG. */
+
+static rtx
+xtensa_function_incoming_arg (cumulative_args_t cum, enum machine_mode mode,
+ const_tree type, bool named ATTRIBUTE_UNUSED)
+{
+ return xtensa_function_arg_1 (cum, mode, type, true);
+}
+
+static unsigned int
+xtensa_function_arg_boundary (enum machine_mode mode, const_tree type)
{
unsigned int alignment;
}
-void
-override_options (void)
+static void
+xtensa_option_override (void)
{
int regno;
enum machine_mode mode;
}
}
-
/* A C compound statement to output to stdio stream STREAM the
assembler syntax for an instruction operand X. X is an RTL
expression.
}
}
+/* Implement TARGET_ASM_OUTPUT_ADDR_CONST_EXTRA. */
-bool
+static bool
xtensa_output_addr_const_extra (FILE *fp, rtx x)
{
if (GET_CODE (x) == UNSPEC && XVECLEN (x, 0) == 1)
note_rtx = gen_rtx_SET (VOIDmode, (frame_pointer_needed
? hard_frame_pointer_rtx
: stack_pointer_rtx),
- plus_constant (stack_pointer_rtx, -total_size));
+ plus_constant (Pmode, stack_pointer_rtx,
+ -total_size));
RTX_FRAME_RELATED_P (insn) = 1;
- REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_FRAME_RELATED_EXPR,
- note_rtx, REG_NOTES (insn));
+ add_reg_note (insn, REG_FRAME_RELATED_EXPR, note_rtx);
}
retaddr = gen_rtx_REG (Pmode, A0_REG);
else
{
- rtx addr = plus_constant (frame, -4 * UNITS_PER_WORD);
+ rtx addr = plus_constant (Pmode, frame, -4 * UNITS_PER_WORD);
addr = memory_address (Pmode, addr);
retaddr = gen_reg_rtx (Pmode);
emit_move_insn (retaddr, gen_rtx_MEM (Pmode, addr));
return result;
}
+/* Disable the use of word-sized or smaller complex modes for structures,
+ and for function arguments in particular, where they cause problems with
+ register a7. The xtensa_copy_incoming_a7 function assumes that there is
+ a single reference to an argument in a7, but with small complex modes the
+ real and imaginary components may be extracted separately, leading to two
+ uses of the register, only one of which would be replaced. */
+
+static bool
+xtensa_member_type_forces_blk (const_tree, enum machine_mode mode)
+{
+ return mode == CQImode || mode == CHImode;
+}
/* Create the va_list data type.
DECL_FIELD_CONTEXT (f_reg) = record;
DECL_FIELD_CONTEXT (f_ndx) = record;
- TREE_CHAIN (record) = type_decl;
+ TYPE_STUB_DECL (record) = type_decl;
TYPE_NAME (record) = type_decl;
TYPE_FIELDS (record) = f_stk;
- TREE_CHAIN (f_stk) = f_reg;
- TREE_CHAIN (f_reg) = f_ndx;
+ DECL_CHAIN (f_stk) = f_reg;
+ DECL_CHAIN (f_reg) = f_ndx;
layout_type (record);
return record;
arg_words = crtl->args.info.arg_words;
f_stk = TYPE_FIELDS (va_list_type_node);
- f_reg = TREE_CHAIN (f_stk);
- f_ndx = TREE_CHAIN (f_reg);
+ f_reg = DECL_CHAIN (f_stk);
+ f_ndx = DECL_CHAIN (f_reg);
stk = build3 (COMPONENT_REF, TREE_TYPE (f_stk), valist, f_stk, NULL_TREE);
reg = build3 (COMPONENT_REF, TREE_TYPE (f_reg), unshare_expr (valist),
/* Set the __va_stk member to ($arg_ptr - 32). */
u = make_tree (ptr_type_node, virtual_incoming_args_rtx);
- u = fold_build2 (POINTER_PLUS_EXPR, ptr_type_node, u, size_int (-32));
+ u = fold_build_pointer_plus_hwi (u, -32);
t = build2 (MODIFY_EXPR, ptr_type_node, stk, u);
TREE_SIDE_EFFECTS (t) = 1;
expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
}
f_stk = TYPE_FIELDS (va_list_type_node);
- f_reg = TREE_CHAIN (f_stk);
- f_ndx = TREE_CHAIN (f_reg);
+ f_reg = DECL_CHAIN (f_stk);
+ f_ndx = DECL_CHAIN (f_reg);
stk = build3 (COMPONENT_REF, TREE_TYPE (f_stk), valist,
f_stk, NULL_TREE);
t = fold_convert (sizetype, unshare_expr (ndx));
t = build2 (MINUS_EXPR, sizetype, t, size);
- addr = build2 (POINTER_PLUS_EXPR, ptr_type_node, unshare_expr (array), t);
+ addr = fold_build_pointer_plus (unshare_expr (array), t);
addr = fold_convert (build_pointer_type (type), addr);
if (indirect)
if (TARGET_THREADPTR)
{
- ftype = build_function_type (ptr_type_node, void_list_node);
+ ftype = build_function_type_list (ptr_type_node, NULL_TREE);
decl = add_builtin_function ("__builtin_thread_pointer", ftype,
XTENSA_BUILTIN_THREAD_POINTER, BUILT_IN_MD,
NULL, NULL_TREE);
return NULL_RTX;
}
+/* Worker function for TARGET_PREFERRED_RELOAD_CLASS. */
-enum reg_class
-xtensa_preferred_reload_class (rtx x, enum reg_class rclass, int isoutput)
+static reg_class_t
+xtensa_preferred_reload_class (rtx x, reg_class_t rclass)
{
- if (!isoutput && CONSTANT_P (x) && GET_CODE (x) == CONST_DOUBLE)
+ if (CONSTANT_P (x) && CONST_DOUBLE_P (x))
return NO_REGS;
/* Don't use the stack pointer or hard frame pointer for reloads!
return rclass;
}
+/* Worker function for TARGET_PREFERRED_OUTPUT_RELOAD_CLASS. */
-enum reg_class
-xtensa_secondary_reload (bool in_p, rtx x, enum reg_class rclass,
+static reg_class_t
+xtensa_preferred_output_reload_class (rtx x ATTRIBUTE_UNUSED,
+ reg_class_t rclass)
+{
+ /* Don't use the stack pointer or hard frame pointer for reloads!
+ The hard frame pointer would normally be OK except that it may
+ briefly hold an incoming argument in the prologue, and reload
+ won't know that it is live because the hard frame pointer is
+ treated specially. */
+
+ if (rclass == AR_REGS || rclass == GR_REGS)
+ return RL_REGS;
+
+ return rclass;
+}
+
+/* Worker function for TARGET_SECONDARY_RELOAD. */
+
+static reg_class_t
+xtensa_secondary_reload (bool in_p, rtx x, reg_class_t rclass,
enum machine_mode mode, secondary_reload_info *sri)
{
int regno;
return function_section (current_function_decl);
}
+/* Worker function for TARGET_REGISTER_MOVE_COST. */
+
+static int
+xtensa_register_move_cost (enum machine_mode mode ATTRIBUTE_UNUSED,
+ reg_class_t from, reg_class_t to)
+{
+ if (from == to && from != BR_REGS && to != BR_REGS)
+ return 2;
+ else if (reg_class_subset_p (from, AR_REGS)
+ && reg_class_subset_p (to, AR_REGS))
+ return 2;
+ else if (reg_class_subset_p (from, AR_REGS) && to == ACC_REG)
+ return 3;
+ else if (from == ACC_REG && reg_class_subset_p (to, AR_REGS))
+ return 3;
+ else
+ return 10;
+}
+
+/* Worker function for TARGET_MEMORY_MOVE_COST. */
+
+static int
+xtensa_memory_move_cost (enum machine_mode mode ATTRIBUTE_UNUSED,
+ reg_class_t rclass ATTRIBUTE_UNUSED,
+ bool in ATTRIBUTE_UNUSED)
+{
+ return 4;
+}
/* Compute a (partial) cost for rtx X. Return true if the complete
cost has been computed, and false if subexpressions should be
scanned. In either case, *TOTAL contains the cost result. */
static bool
-xtensa_rtx_costs (rtx x, int code, int outer_code, int *total,
- bool speed ATTRIBUTE_UNUSED)
+xtensa_rtx_costs (rtx x, int code, int outer_code, int opno ATTRIBUTE_UNUSED,
+ int *total, bool speed ATTRIBUTE_UNUSED)
{
switch (code)
{
outgoing ? GP_OUTGOING_RETURN : GP_RETURN);
}
+/* Worker function for TARGET_LIBCALL_VALUE. */
+
+static rtx
+xtensa_libcall_value (enum machine_mode mode, const_rtx fun ATTRIBUTE_UNUSED)
+{
+ return gen_rtx_REG ((GET_MODE_CLASS (mode) == MODE_INT
+ && GET_MODE_SIZE (mode) < UNITS_PER_WORD)
+ ? SImode : mode, GP_RETURN);
+}
+
+/* Worker function TARGET_FUNCTION_VALUE_REGNO_P. */
+
+static bool
+xtensa_function_value_regno_p (const unsigned int regno)
+{
+ return (regno == GP_RETURN);
+}
+
/* The static chain is passed in memory. Provide rtx giving 'mem'
expressions that denote where they are stored. */
xtensa_static_chain (const_tree ARG_UNUSED (fndecl), bool incoming_p)
{
rtx base = incoming_p ? arg_pointer_rtx : stack_pointer_rtx;
- return gen_frame_mem (Pmode, plus_constant (base, -5 * UNITS_PER_WORD));
+ return gen_frame_mem (Pmode, plus_constant (Pmode, base,
+ -5 * UNITS_PER_WORD));
}
emit_move_insn (adjust_address (m_tramp, SImode, chain_off), chain);
emit_move_insn (adjust_address (m_tramp, SImode, func_off), func);
emit_library_call (gen_rtx_SYMBOL_REF (Pmode, "__xtensa_sync_caches"),
- 0, VOIDmode, 1, XEXP (m_tramp, 0), Pmode);
+ LCT_NORMAL, VOIDmode, 1, XEXP (m_tramp, 0), Pmode);
}
+/* Implement TARGET_LEGITIMATE_CONSTANT_P. */
+
+static bool
+xtensa_legitimate_constant_p (enum machine_mode mode ATTRIBUTE_UNUSED, rtx x)
+{
+ return !xtensa_tls_referenced_p (x);
+}
#include "gt-xtensa.h"