/* Subroutines used for code generation on Renesas RX processors.
- Copyright (C) 2008, 2009, 2010 Free Software Foundation, Inc.
+ Copyright (C) 2008, 2009, 2010, 2011 Free Software Foundation, Inc.
Contributed by Red Hat.
This file is part of GCC.
#include "target.h"
#include "target-def.h"
#include "langhooks.h"
+#include "opts.h"
+
+static unsigned int rx_gp_base_regnum_val = INVALID_REGNUM;
+static unsigned int rx_pid_base_regnum_val = INVALID_REGNUM;
+static unsigned int rx_num_interrupt_regs;
\f
+static unsigned int
+rx_gp_base_regnum (void)
+{
+ if (rx_gp_base_regnum_val == INVALID_REGNUM)
+ gcc_unreachable ();
+ return rx_gp_base_regnum_val;
+}
+
+static unsigned int
+rx_pid_base_regnum (void)
+{
+ if (rx_pid_base_regnum_val == INVALID_REGNUM)
+ gcc_unreachable ();
+ return rx_pid_base_regnum_val;
+}
+
+/* Find a SYMBOL_REF in a "standard" MEM address and return its decl. */
+
+static tree
+rx_decl_for_addr (rtx op)
+{
+ if (GET_CODE (op) == MEM)
+ op = XEXP (op, 0);
+ if (GET_CODE (op) == CONST)
+ op = XEXP (op, 0);
+ while (GET_CODE (op) == PLUS)
+ op = XEXP (op, 0);
+ if (GET_CODE (op) == SYMBOL_REF)
+ return SYMBOL_REF_DECL (op);
+ return NULL_TREE;
+}
+
static void rx_print_operand (FILE *, rtx, int);
#define CC_FLAG_S (1 << 0)
#define CC_FLAG_Z (1 << 1)
#define CC_FLAG_O (1 << 2)
#define CC_FLAG_C (1 << 3)
-#define CC_FLAG_FP (1 << 4) /* fake, to differentiate CC_Fmode */
+#define CC_FLAG_FP (1 << 4) /* Fake, to differentiate CC_Fmode. */
static unsigned int flags_from_mode (enum machine_mode mode);
static unsigned int flags_from_code (enum rtx_code code);
-
-enum rx_cpu_types rx_cpu_type = RX600;
\f
+/* Return true if OP is a reference to an object in a PID data area. */
+
+enum pid_type
+{
+ PID_NOT_PID = 0, /* The object is not in the PID data area. */
+ PID_ENCODED, /* The object is in the PID data area. */
+ PID_UNENCODED /* The object will be placed in the PID data area, but it has not been placed there yet. */
+};
+
+static enum pid_type
+rx_pid_data_operand (rtx op)
+{
+ tree op_decl;
+
+ if (!TARGET_PID)
+ return PID_NOT_PID;
+
+ if (GET_CODE (op) == PLUS
+ && GET_CODE (XEXP (op, 0)) == REG
+ && GET_CODE (XEXP (op, 1)) == CONST
+ && GET_CODE (XEXP (XEXP (op, 1), 0)) == UNSPEC)
+ return PID_ENCODED;
+
+ op_decl = rx_decl_for_addr (op);
+
+ if (op_decl)
+ {
+ if (TREE_READONLY (op_decl))
+ return PID_UNENCODED;
+ }
+ else
+ {
+ /* Sigh, some special cases. */
+ if (GET_CODE (op) == SYMBOL_REF
+ || GET_CODE (op) == LABEL_REF)
+ return PID_UNENCODED;
+ }
+
+ return PID_NOT_PID;
+}
+
+static rtx
+rx_legitimize_address (rtx x,
+ rtx oldx ATTRIBUTE_UNUSED,
+ enum machine_mode mode ATTRIBUTE_UNUSED)
+{
+ if (rx_pid_data_operand (x) == PID_UNENCODED)
+ {
+ rtx rv = gen_pid_addr (gen_rtx_REG (SImode, rx_pid_base_regnum ()), x);
+ return rv;
+ }
+
+ if (GET_CODE (x) == PLUS
+ && GET_CODE (XEXP (x, 0)) == PLUS
+ && REG_P (XEXP (XEXP (x, 0), 0))
+ && REG_P (XEXP (x, 1)))
+ return force_reg (SImode, x);
+
+ return x;
+}
+
/* Return true if OP is a reference to an object in a small data area. */
static bool
}
static bool
-rx_is_legitimate_address (Mmode mode, rtx x, bool strict ATTRIBUTE_UNUSED)
+rx_is_legitimate_address (enum machine_mode mode, rtx x,
+ bool strict ATTRIBUTE_UNUSED)
{
if (RTX_OK_FOR_BASE (x, strict))
/* Register Indirect. */
return true;
- if (GET_MODE_SIZE (mode) == 4
+ if ((GET_MODE_SIZE (mode) == 4
+ || GET_MODE_SIZE (mode) == 2
+ || GET_MODE_SIZE (mode) == 1)
&& (GET_CODE (x) == PRE_DEC || GET_CODE (x) == POST_INC))
/* Pre-decrement Register Indirect or
Post-increment Register Indirect. */
return RTX_OK_FOR_BASE (XEXP (x, 0), strict);
+ switch (rx_pid_data_operand (x))
+ {
+ case PID_UNENCODED:
+ return false;
+ case PID_ENCODED:
+ return true;
+ default:
+ break;
+ }
+
if (GET_CODE (x) == PLUS)
{
rtx arg1 = XEXP (x, 0);
if (val < 0)
return false;
-
+
switch (GET_MODE_SIZE (mode))
{
default:
bool
rx_is_restricted_memory_address (rtx mem, enum machine_mode mode)
{
- rtx base, index;
-
if (! rx_is_legitimate_address
(mode, mem, reload_in_progress || reload_completed))
return false;
return false;
case PLUS:
- /* Only allow REG+INT addressing. */
- base = XEXP (mem, 0);
- index = XEXP (mem, 1);
+ {
+ rtx base, index;
+
+ /* Only allow REG+INT addressing. */
+ base = XEXP (mem, 0);
+ index = XEXP (mem, 1);
- return RX_REG_P (base) && CONST_INT_P (index);
+ if (! RX_REG_P (base) || ! CONST_INT_P (index))
+ return false;
+
+ return IN_RANGE (INTVAL (index), 0, (0x10000 * GET_MODE_SIZE (mode)) - 1);
+ }
case SYMBOL_REF:
/* Can happen when small data is being supported.
}
}
-bool
-rx_is_mode_dependent_addr (rtx addr)
+/* Implement TARGET_MODE_DEPENDENT_ADDRESS_P. */
+
+static bool
+rx_mode_dependent_address_p (const_rtx addr)
{
if (GET_CODE (addr) == CONST)
addr = XEXP (addr, 0);
break;
}
+ case CONST:
+ if (GET_CODE (XEXP (addr, 0)) == UNSPEC)
+ {
+ addr = XEXP (addr, 0);
+ gcc_assert (XINT (addr, 1) == UNSPEC_CONST);
+
+ /* FIXME: Putting this case label here is an appalling abuse of the C language. */
+ case UNSPEC:
+ addr = XVECEXP (addr, 0, 0);
+ gcc_assert (CONST_INT_P (addr));
+ }
+ /* Fall through. */
case LABEL_REF:
case SYMBOL_REF:
- case CONST:
fprintf (file, "#");
+ /* Fall through. */
default:
output_addr_const (file, addr);
break;
%B Print an integer comparison name.
%C Print a control register name.
%F Print a condition code flag name.
+ %G Register used for small-data-area addressing
%H Print high part of a DImode register, integer or address.
%L Print low part of a DImode register, integer or address.
%N Print the negation of the immediate value.
+ %P Register used for PID addressing
%Q If the operand is a MEM, then correctly generate
- register indirect or register relative addressing. */
+ register indirect or register relative addressing.
+ %R Like %Q but for zero-extending loads. */
static void
rx_print_operand (FILE * file, rtx op, int letter)
{
+ bool unsigned_load = false;
+ bool print_hash = true;
+
+ if (letter == 'A'
+ && ((GET_CODE (op) == CONST
+ && GET_CODE (XEXP (op, 0)) == UNSPEC)
+ || GET_CODE (op) == UNSPEC))
+ {
+ print_hash = false;
+ letter = 0;
+ }
+
switch (letter)
{
case 'A':
}
else
{
+ unsigned int flags = flags_from_mode (mode);
+
switch (code)
{
case LT:
- ret = "n";
+ ret = (flags & CC_FLAG_O ? "lt" : "n");
break;
case GE:
- ret = "pz";
+ ret = (flags & CC_FLAG_O ? "ge" : "pz");
break;
case GT:
ret = "gt";
default:
gcc_unreachable ();
}
- gcc_checking_assert ((flags_from_code (code)
- & ~flags_from_mode (mode)) == 0);
+ gcc_checking_assert ((flags_from_code (code) & ~flags) == 0);
}
fputs (ret, file);
break;
case 0xb: fprintf (file, "fintv"); break;
case 0xc: fprintf (file, "intb"); break;
default:
- warning (0, "unreocgnized control register number: %d - using 'psw'",
+ warning (0, "unrecognized control register number: %d - using 'psw'",
(int) INTVAL (op));
fprintf (file, "psw");
break;
}
break;
+ case 'G':
+ fprintf (file, "%s", reg_names [rx_gp_base_regnum ()]);
+ break;
+
case 'H':
switch (GET_CODE (op))
{
rx_print_integer (file, - INTVAL (op));
break;
+ case 'P':
+ fprintf (file, "%s", reg_names [rx_pid_base_regnum ()]);
+ break;
+
+ case 'R':
+ gcc_assert (GET_MODE_SIZE (GET_MODE (op)) < 4);
+ unsigned_load = true;
+ /* Fall through. */
case 'Q':
if (MEM_P (op))
{
HOST_WIDE_INT offset;
+ rtx mem = op;
op = XEXP (op, 0);
rx_print_operand (file, op, 0);
fprintf (file, "].");
- switch (GET_MODE_SIZE (GET_MODE (op)))
+ switch (GET_MODE_SIZE (GET_MODE (mem)))
{
case 1:
- gcc_assert (offset < 65535 * 1);
- fprintf (file, "B");
+ gcc_assert (offset <= 65535 * 1);
+ fprintf (file, unsigned_load ? "UB" : "B");
break;
case 2:
gcc_assert (offset % 2 == 0);
- gcc_assert (offset < 65535 * 2);
- fprintf (file, "W");
+ gcc_assert (offset <= 65535 * 2);
+ fprintf (file, unsigned_load ? "UW" : "W");
break;
- default:
+ case 4:
gcc_assert (offset % 4 == 0);
- gcc_assert (offset < 65535 * 4);
+ gcc_assert (offset <= 65535 * 4);
fprintf (file, "L");
break;
+ default:
+ gcc_unreachable ();
}
break;
}
/* Fall through. */
default:
+ if (GET_CODE (op) == CONST
+ && GET_CODE (XEXP (op, 0)) == UNSPEC)
+ op = XEXP (op, 0);
+ else if (GET_CODE (op) == CONST
+ && GET_CODE (XEXP (op, 0)) == PLUS
+ && GET_CODE (XEXP (XEXP (op, 0), 0)) == UNSPEC
+ && GET_CODE (XEXP (XEXP (op, 0), 1)) == CONST_INT)
+ {
+ if (print_hash)
+ fprintf (file, "#");
+ fprintf (file, "(");
+ rx_print_operand (file, XEXP (XEXP (op, 0), 0), 'A');
+ fprintf (file, " + ");
+ output_addr_const (file, XEXP (XEXP (op, 0), 1));
+ fprintf (file, ")");
+ return;
+ }
+
switch (GET_CODE (op))
{
case MULT:
REAL_VALUE_FROM_CONST_DOUBLE (rv, op);
REAL_VALUE_TO_TARGET_SINGLE (rv, val);
- fprintf (file, TARGET_AS100_SYNTAX ? "#0%lxH" : "#0x%lx", val);
+ if (print_hash)
+ fprintf (file, "#");
+ fprintf (file, TARGET_AS100_SYNTAX ? "0%lxH" : "0x%lx", val);
break;
}
case CONST_INT:
- fprintf (file, "#");
+ if (print_hash)
+ fprintf (file, "#");
rx_print_integer (file, INTVAL (op));
break;
- case SYMBOL_REF:
+ case UNSPEC:
+ switch (XINT (op, 1))
+ {
+ case UNSPEC_PID_ADDR:
+ {
+ rtx sym, add;
+
+ if (print_hash)
+ fprintf (file, "#");
+ sym = XVECEXP (op, 0, 0);
+ add = NULL_RTX;
+ fprintf (file, "(");
+ if (GET_CODE (sym) == PLUS)
+ {
+ add = XEXP (sym, 1);
+ sym = XEXP (sym, 0);
+ }
+ output_addr_const (file, sym);
+ if (add != NULL_RTX)
+ {
+ fprintf (file, "+");
+ output_addr_const (file, add);
+ }
+ fprintf (file, "-__pid_base");
+ fprintf (file, ")");
+ return;
+ }
+ }
+ /* Fall through */
+
case CONST:
+ case SYMBOL_REF:
case LABEL_REF:
case CODE_LABEL:
- case UNSPEC:
rx_print_operand_address (file, op);
break;
}
}
+/* Maybe convert an operand into its PID format. */
+
+rtx
+rx_maybe_pidify_operand (rtx op, int copy_to_reg)
+{
+ if (rx_pid_data_operand (op) == PID_UNENCODED)
+ {
+ if (GET_CODE (op) == MEM)
+ {
+ rtx a = gen_pid_addr (gen_rtx_REG (SImode, rx_pid_base_regnum ()), XEXP (op, 0));
+ op = replace_equiv_address (op, a);
+ }
+ else
+ {
+ op = gen_pid_addr (gen_rtx_REG (SImode, rx_pid_base_regnum ()), op);
+ }
+
+ if (copy_to_reg)
+ op = copy_to_mode_reg (GET_MODE (op), op);
+ }
+ return op;
+}
+
/* Returns an assembler template for a move instruction. */
char *
gcc_unreachable ();
}
- if (MEM_P (src) && rx_small_data_operand (XEXP (src, 0)))
- src_template = "%%gp(%A1)[r13]";
+ if (MEM_P (src) && rx_pid_data_operand (XEXP (src, 0)) == PID_UNENCODED)
+ src_template = "(%A1-__pid_base)[%P1]";
+ else if (MEM_P (src) && rx_small_data_operand (XEXP (src, 0)))
+ src_template = "%%gp(%A1)[%G1]";
else
src_template = "%1";
if (MEM_P (dest) && rx_small_data_operand (XEXP (dest, 0)))
- dst_template = "%%gp(%A0)[r13]";
+ dst_template = "%%gp(%A0)[%G0]";
else
dst_template = "%0";
occupied by an argument of type TYPE and mode MODE. */
static unsigned int
-rx_function_arg_size (Mmode mode, const_tree type)
+rx_function_arg_size (enum machine_mode mode, const_tree type)
{
unsigned int num_bytes;
variable parameter list. */
static rtx
-rx_function_arg (Fargs * cum, Mmode mode, const_tree type, bool named)
+rx_function_arg (cumulative_args_t cum, enum machine_mode mode,
+ const_tree type, bool named)
{
unsigned int next_reg;
- unsigned int bytes_so_far = *cum;
+ unsigned int bytes_so_far = *get_cumulative_args (cum);
unsigned int size;
unsigned int rounded_size;
}
static void
-rx_function_arg_advance (Fargs * cum, Mmode mode, const_tree type,
- bool named ATTRIBUTE_UNUSED)
+rx_function_arg_advance (cumulative_args_t cum, enum machine_mode mode,
+ const_tree type, bool named ATTRIBUTE_UNUSED)
{
- *cum += rx_function_arg_size (mode, type);
+ *get_cumulative_args (cum) += rx_function_arg_size (mode, type);
}
static unsigned int
-rx_function_arg_boundary (Mmode mode ATTRIBUTE_UNUSED,
+rx_function_arg_boundary (enum machine_mode mode ATTRIBUTE_UNUSED,
const_tree type ATTRIBUTE_UNUSED)
{
return 32;
/* RX ABI specifies that small integer types are
promoted to int when returned by a function. */
- if (GET_MODE_SIZE (mode) > 0 && GET_MODE_SIZE (mode) < 4)
+ if (GET_MODE_SIZE (mode) > 0
+ && GET_MODE_SIZE (mode) < 4
+ && ! COMPLEX_MODE_P (mode)
+ )
return gen_rtx_REG (SImode, FUNC_RETURN_REGNUM);
return gen_rtx_REG (mode, FUNC_RETURN_REGNUM);
{
if (for_return != 1
|| GET_MODE_SIZE (mode) >= 4
+ || COMPLEX_MODE_P (mode)
|| GET_MODE_SIZE (mode) < 1)
return mode;
{
static bool using_fixed_regs = false;
+ if (TARGET_PID)
+ {
+ rx_pid_base_regnum_val = GP_BASE_REGNUM - rx_num_interrupt_regs;
+ fixed_regs[rx_pid_base_regnum_val] = call_used_regs [rx_pid_base_regnum_val] = 1;
+ }
+
if (rx_small_data_limit > 0)
- fixed_regs[GP_BASE_REGNUM] = call_used_regs [GP_BASE_REGNUM] = 1;
+ {
+ if (TARGET_PID)
+ rx_gp_base_regnum_val = rx_pid_base_regnum_val - 1;
+ else
+ rx_gp_base_regnum_val = GP_BASE_REGNUM - rx_num_interrupt_regs;
+
+ fixed_regs[rx_gp_base_regnum_val] = call_used_regs [rx_gp_base_regnum_val] = 1;
+ }
if (use_fixed_regs != using_fixed_regs)
{
for (save_mask = high = low = 0, reg = 1; reg < CC_REGNUM; reg++)
{
if ((df_regs_ever_live_p (reg)
- /* Always save all call clobbered registers inside interrupt
- handlers, even if they are not live - they may be used in
- routines called from this one. */
- || (call_used_regs[reg] && is_interrupt_func (NULL_TREE)))
+ /* Always save all call clobbered registers inside non-leaf
+ interrupt handlers, even if they are not live - they may
+ be used in (non-interrupt aware) routines called from this one. */
+ || (call_used_regs[reg]
+ && is_interrupt_func (NULL_TREE)
+ && ! current_function_is_leaf))
&& (! call_used_regs[reg]
/* Even call clobbered registered must
be pushed inside interrupt handlers. */
}
}
+static bool
+ok_for_max_constant (HOST_WIDE_INT val)
+{
+ if (rx_max_constant_size == 0 || rx_max_constant_size == 4)
+ /* If there is no constraint on the size of constants
+ used as operands, then any value is legitimate. */
+ return true;
+
+ /* rx_max_constant_size specifies the maximum number
+ of bytes that can be used to hold a signed value. */
+ return IN_RANGE (val, (-1 << (rx_max_constant_size * 8)),
+ ( 1 << (rx_max_constant_size * 8)));
+}
+
+/* Generate an ADD of SRC plus VAL into DEST.
+ Handles the case where VAL is too big for max_constant_value.
+ Sets FRAME_RELATED_P on the insn if IS_FRAME_RELATED is true. */
+
+static void
+gen_safe_add (rtx dest, rtx src, rtx val, bool is_frame_related)
+{
+ rtx insn;
+
+ if (val == NULL_RTX || INTVAL (val) == 0)
+ {
+ gcc_assert (dest != src);
+
+ insn = emit_move_insn (dest, src);
+ }
+ else if (ok_for_max_constant (INTVAL (val)))
+ insn = emit_insn (gen_addsi3 (dest, src, val));
+ else
+ {
+ /* Wrap VAL in an UNSPEC so that rx_is_legitimate_constant
+ will not reject it. */
+ val = gen_rtx_CONST (SImode, gen_rtx_UNSPEC (SImode, gen_rtvec (1, val), UNSPEC_CONST));
+ insn = emit_insn (gen_addsi3 (dest, src, val));
+
+ if (is_frame_related)
+ /* We have to provide our own frame related note here
+ as the dwarf2out code cannot be expected to grok
+ our unspec. */
+ add_reg_note (insn, REG_FRAME_RELATED_EXPR,
+ gen_rtx_SET (SImode, dest,
+ gen_rtx_PLUS (SImode, src, val)));
+ return;
+ }
+
+ if (is_frame_related)
+ RTX_FRAME_RELATED_P (insn) = 1;
+ return;
+}
+
void
rx_expand_prologue (void)
{
/* If needed, set up the frame pointer. */
if (frame_pointer_needed)
- {
- if (frame_size)
- insn = emit_insn (gen_addsi3 (frame_pointer_rtx, stack_pointer_rtx,
- GEN_INT (- (HOST_WIDE_INT) frame_size)));
- else
- insn = emit_move_insn (frame_pointer_rtx, stack_pointer_rtx);
-
- RTX_FRAME_RELATED_P (insn) = 1;
- }
-
- insn = NULL_RTX;
+ gen_safe_add (frame_pointer_rtx, stack_pointer_rtx,
+ GEN_INT (- (HOST_WIDE_INT) frame_size), true);
/* Allocate space for the outgoing args.
If the stack frame has not already been set up then handle this as well. */
if (frame_size)
{
if (frame_pointer_needed)
- insn = emit_insn (gen_addsi3 (stack_pointer_rtx, frame_pointer_rtx,
- GEN_INT (- (HOST_WIDE_INT)
- stack_size)));
+ gen_safe_add (stack_pointer_rtx, frame_pointer_rtx,
+ GEN_INT (- (HOST_WIDE_INT) stack_size), true);
else
- insn = emit_insn (gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx,
- GEN_INT (- (HOST_WIDE_INT)
- (frame_size + stack_size))));
+ gen_safe_add (stack_pointer_rtx, stack_pointer_rtx,
+ GEN_INT (- (HOST_WIDE_INT) (frame_size + stack_size)),
+ true);
}
else
- insn = emit_insn (gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx,
- GEN_INT (- (HOST_WIDE_INT) stack_size)));
+ gen_safe_add (stack_pointer_rtx, stack_pointer_rtx,
+ GEN_INT (- (HOST_WIDE_INT) stack_size), true);
}
else if (frame_size)
{
if (! frame_pointer_needed)
- insn = emit_insn (gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx,
- GEN_INT (- (HOST_WIDE_INT) frame_size)));
+ gen_safe_add (stack_pointer_rtx, stack_pointer_rtx,
+ GEN_INT (- (HOST_WIDE_INT) frame_size), true);
else
- insn = emit_move_insn (stack_pointer_rtx, frame_pointer_rtx);
+ gen_safe_add (stack_pointer_rtx, frame_pointer_rtx, NULL_RTX,
+ true);
}
-
- if (insn != NULL_RTX)
- RTX_FRAME_RELATED_P (insn) = 1;
}
static void
: plus_constant (stack_pointer_rtx,
i * UNITS_PER_WORD)));
- XVECEXP (vector, 0, count - 1) = gen_rtx_RETURN (VOIDmode);
+ XVECEXP (vector, 0, count - 1) = ret_rtx;
return vector;
}
{
/* Cannot use the special instructions - deconstruct by hand. */
if (total_size)
- emit_insn (gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx,
- GEN_INT (total_size)));
+ gen_safe_add (stack_pointer_rtx, stack_pointer_rtx,
+ GEN_INT (total_size), false);
if (MUST_SAVE_ACC_REGISTER)
{
return;
}
- emit_insn (gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx,
- GEN_INT (total_size)));
+ gen_safe_add (stack_pointer_rtx, stack_pointer_rtx,
+ GEN_INT (total_size), false);
}
if (low)
RX_BUILTIN_max
};
+static GTY(()) tree rx_builtins[(int) RX_BUILTIN_max];
+
static void
rx_init_builtins (void)
{
#define ADD_RX_BUILTIN1(UC_NAME, LC_NAME, RET_TYPE, ARG_TYPE) \
- add_builtin_function ("__builtin_rx_" LC_NAME, \
+ rx_builtins[RX_BUILTIN_##UC_NAME] = \
+ add_builtin_function ("__builtin_rx_" LC_NAME, \
build_function_type_list (RET_TYPE##_type_node, \
ARG_TYPE##_type_node, \
NULL_TREE), \
BUILT_IN_MD, NULL, NULL_TREE)
#define ADD_RX_BUILTIN2(UC_NAME, LC_NAME, RET_TYPE, ARG_TYPE1, ARG_TYPE2) \
+ rx_builtins[RX_BUILTIN_##UC_NAME] = \
add_builtin_function ("__builtin_rx_" LC_NAME, \
build_function_type_list (RET_TYPE##_type_node, \
ARG_TYPE1##_type_node,\
BUILT_IN_MD, NULL, NULL_TREE)
#define ADD_RX_BUILTIN3(UC_NAME,LC_NAME,RET_TYPE,ARG_TYPE1,ARG_TYPE2,ARG_TYPE3) \
+ rx_builtins[RX_BUILTIN_##UC_NAME] = \
add_builtin_function ("__builtin_rx_" LC_NAME, \
build_function_type_list (RET_TYPE##_type_node, \
ARG_TYPE1##_type_node,\
ADD_RX_BUILTIN1 (WAIT, "wait", void, void);
}
+/* Return the RX builtin for CODE. */
+
+static tree
+rx_builtin_decl (unsigned code, bool initialize_p ATTRIBUTE_UNUSED)
+{
+ if (code >= RX_BUILTIN_max)
+ return error_mark_node;
+
+ return rx_builtins[code];
+}
+
static rtx
rx_expand_void_builtin_1_arg (rtx arg, rtx (* gen_func)(rtx), bool reg)
{
/* Table of RX specific attributes. */
const struct attribute_spec rx_attribute_table[] =
{
- /* Name, min_len, max_len, decl_req, type_req, fn_type_req, handler. */
- { "fast_interrupt", 0, 0, true, false, false, rx_handle_func_attribute },
- { "interrupt", 0, 0, true, false, false, rx_handle_func_attribute },
- { "naked", 0, 0, true, false, false, rx_handle_func_attribute },
- { NULL, 0, 0, false, false, false, NULL }
+ /* Name, min_len, max_len, decl_req, type_req, fn_type_req, handler,
+ affects_type_identity. */
+ { "fast_interrupt", 0, 0, true, false, false, rx_handle_func_attribute,
+ false },
+ { "interrupt", 0, 0, true, false, false, rx_handle_func_attribute,
+ false },
+ { "naked", 0, 0, true, false, false, rx_handle_func_attribute,
+ false },
+ { NULL, 0, 0, false, false, false, NULL, false }
};
-/* Extra processing for target specific command line options. */
-
-static bool
-rx_handle_option (size_t code, const char * arg ATTRIBUTE_UNUSED, int value)
-{
- switch (code)
- {
- case OPT_mint_register_:
- switch (value)
- {
- case 4:
- fixed_regs[10] = call_used_regs [10] = 1;
- /* Fall through. */
- case 3:
- fixed_regs[11] = call_used_regs [11] = 1;
- /* Fall through. */
- case 2:
- fixed_regs[12] = call_used_regs [12] = 1;
- /* Fall through. */
- case 1:
- fixed_regs[13] = call_used_regs [13] = 1;
- /* Fall through. */
- case 0:
- return true;
- default:
- return false;
- }
- break;
-
- case OPT_mmax_constant_size_:
- /* Make sure that the -mmax-constant_size option is in range. */
- return value >= 0 && value <= 4;
-
- case OPT_mcpu_:
- if (strcasecmp (arg, "RX610") == 0)
- rx_cpu_type = RX610;
- else if (strcasecmp (arg, "RX200") == 0)
- {
- target_flags |= MASK_NO_USE_FPU;
- rx_cpu_type = RX200;
- }
- else if (strcasecmp (arg, "RX600") != 0)
- warning (0, "unrecognized argument '%s' to -mcpu= option", arg);
- break;
-
- case OPT_fpu:
- if (rx_cpu_type == RX200)
- error ("the RX200 cpu does not have FPU hardware");
- break;
-
- default:
- break;
- }
-
- return true;
-}
-
/* Implement TARGET_OVERRIDE_OPTIONS_AFTER_CHANGE. */
static void
static void
rx_option_override (void)
{
+ unsigned int i;
+ cl_deferred_option *opt;
+ VEC(cl_deferred_option,heap) *vec
+ = (VEC(cl_deferred_option,heap) *) rx_deferred_options;
+
+ FOR_EACH_VEC_ELT (cl_deferred_option, vec, i, opt)
+ {
+ switch (opt->opt_index)
+ {
+ case OPT_mint_register_:
+ switch (opt->value)
+ {
+ case 4:
+ fixed_regs[10] = call_used_regs [10] = 1;
+ /* Fall through. */
+ case 3:
+ fixed_regs[11] = call_used_regs [11] = 1;
+ /* Fall through. */
+ case 2:
+ fixed_regs[12] = call_used_regs [12] = 1;
+ /* Fall through. */
+ case 1:
+ fixed_regs[13] = call_used_regs [13] = 1;
+ /* Fall through. */
+ case 0:
+ rx_num_interrupt_regs = opt->value;
+ break;
+ default:
+ rx_num_interrupt_regs = 0;
+ /* Error message already given because rx_handle_option
+ returned false. */
+ break;
+ }
+ break;
+
+ default:
+ gcc_unreachable ();
+ }
+ }
+
/* This target defaults to strict volatile bitfields. */
- if (flag_strict_volatile_bitfields < 0)
+ if (flag_strict_volatile_bitfields < 0 && abi_version_at_least(2))
flag_strict_volatile_bitfields = 1;
rx_override_options_after_change ();
-}
-/* Implement TARGET_OPTION_OPTIMIZATION_TABLE. */
-static const struct default_options rx_option_optimization_table[] =
- {
- { OPT_LEVELS_1_PLUS, OPT_fomit_frame_pointer, NULL, 1 },
- { OPT_LEVELS_NONE, 0, NULL, 0 }
- };
+ if (align_jumps == 0 && ! optimize_size)
+ align_jumps = 3;
+ if (align_loops == 0 && ! optimize_size)
+ align_loops = 3;
+ if (align_labels == 0 && ! optimize_size)
+ align_labels = 3;
+}
\f
static bool
operand on the RX. X is already known to satisfy CONSTANT_P. */
bool
-rx_is_legitimate_constant (rtx x)
+rx_is_legitimate_constant (enum machine_mode mode ATTRIBUTE_UNUSED, rtx x)
{
- HOST_WIDE_INT val;
-
switch (GET_CODE (x))
{
case CONST:
case SYMBOL_REF:
return true;
- /* One day we may have to handle UNSPEC constants here. */
+ case UNSPEC:
+ return XINT (x, 1) == UNSPEC_CONST || XINT (x, 1) == UNSPEC_PID_ADDR;
+
default:
/* FIXME: Can this ever happen ? */
- abort ();
- return false;
+ gcc_unreachable ();
}
break;
break;
}
- if (rx_max_constant_size == 0 || rx_max_constant_size == 4)
- /* If there is no constraint on the size of constants
- used as operands, then any value is legitimate. */
- return true;
-
- val = INTVAL (x);
-
- /* rx_max_constant_size specifies the maximum number
- of bytes that can be used to hold a signed value. */
- return IN_RANGE (val, (-1 << (rx_max_constant_size * 8)),
- ( 1 << (rx_max_constant_size * 8)));
+ return ok_for_max_constant (INTVAL (x));
}
static int
}
\f
static int
-rx_memory_move_cost (enum machine_mode mode, reg_class_t regclass, bool in)
+rx_memory_move_cost (enum machine_mode mode ATTRIBUTE_UNUSED,
+ reg_class_t regclass ATTRIBUTE_UNUSED,
+ bool in)
{
- return 2 + memory_move_secondary_cost (mode, regclass, in);
+ return (in ? 2 : 0) + REGISTER_MOVE_COST (mode, regclass, regclass);
}
/* Convert a CC_MODE to the set of flags that it represents. */
/* Return the minimal CC mode needed to implement (CMP_CODE X Y). */
enum machine_mode
-rx_select_cc_mode (enum rtx_code cmp_code, rtx x, rtx y ATTRIBUTE_UNUSED)
+rx_select_cc_mode (enum rtx_code cmp_code, rtx x, rtx y)
{
if (GET_MODE_CLASS (GET_MODE (x)) == MODE_FLOAT)
return CC_Fmode;
- return mode_from_flags (flags_from_code (cmp_code));
-}
-
-/* Split the floating-point comparison IN into individual comparisons
- O1 and O2. O2 may be UNKNOWN if there is no second comparison.
- Return true iff the comparison operands must be swapped. */
-
-bool
-rx_split_fp_compare (enum rtx_code in, enum rtx_code *o1, enum rtx_code *o2)
-{
- enum rtx_code cmp1 = in, cmp2 = UNKNOWN;
- bool swap = false;
-
- switch (in)
- {
- case ORDERED:
- case UNORDERED:
- case LT:
- case GE:
- case EQ:
- case NE:
- break;
-
- case GT:
- case LE:
- cmp1 = swap_condition (cmp1);
- swap = true;
- break;
-
- case UNEQ:
- cmp1 = UNORDERED;
- cmp2 = EQ;
- break;
- case UNLT:
- cmp1 = UNORDERED;
- cmp2 = LT;
- break;
- case UNGE:
- cmp1 = UNORDERED;
- cmp2 = GE;
- break;
- case UNLE:
- cmp1 = UNORDERED;
- cmp2 = GT;
- swap = true;
- break;
- case UNGT:
- cmp1 = UNORDERED;
- cmp2 = LE;
- swap = true;
- break;
- case LTGT:
- cmp1 = ORDERED;
- cmp2 = NE;
- break;
+ if (y != const0_rtx)
+ return CCmode;
- default:
- gcc_unreachable ();
- }
-
- *o1 = cmp1;
- *o2 = cmp2;
- return swap;
+ return mode_from_flags (flags_from_code (cmp_code));
}
/* Split the conditional branch. Emit (COMPARE C1 C2) into CC_REG with
return true;
}
+\f
+int
+rx_align_for_label (rtx lab, int uses_threshold)
+{
+ /* This is a simple heuristic to guess when an alignment would not be useful
+ because the delay due to the inserted NOPs would be greater than the delay
+ due to the misaligned branch. If uses_threshold is zero then the alignment
+ is always useful. */
+ if (LABEL_P (lab) && LABEL_NUSES (lab) < uses_threshold)
+ return 0;
+ return optimize_size ? 1 : 3;
+}
+
+static int
+rx_max_skip_for_label (rtx lab)
+{
+ int opsize;
+ rtx op;
+
+ if (lab == NULL_RTX)
+ return 0;
+
+ op = lab;
+ do
+ {
+ op = next_nonnote_nondebug_insn (op);
+ }
+ while (op && (LABEL_P (op)
+ || (INSN_P (op) && GET_CODE (PATTERN (op)) == USE)));
+ if (!op)
+ return 0;
+
+ opsize = get_attr_length (op);
+ if (opsize >= 0 && opsize < 8)
+ return opsize - 1;
+ return 0;
+}
+
+/* Compute the real length of the extending load-and-op instructions. */
+
+int
+rx_adjust_insn_length (rtx insn, int current_length)
+{
+ rtx extend, mem, offset;
+ bool zero;
+ int factor;
+
+ switch (INSN_CODE (insn))
+ {
+ default:
+ return current_length;
+
+ case CODE_FOR_plussi3_zero_extendhi:
+ case CODE_FOR_andsi3_zero_extendhi:
+ case CODE_FOR_iorsi3_zero_extendhi:
+ case CODE_FOR_xorsi3_zero_extendhi:
+ case CODE_FOR_divsi3_zero_extendhi:
+ case CODE_FOR_udivsi3_zero_extendhi:
+ case CODE_FOR_minussi3_zero_extendhi:
+ case CODE_FOR_smaxsi3_zero_extendhi:
+ case CODE_FOR_sminsi3_zero_extendhi:
+ case CODE_FOR_multsi3_zero_extendhi:
+ case CODE_FOR_comparesi3_zero_extendhi:
+ zero = true;
+ factor = 2;
+ break;
+
+ case CODE_FOR_plussi3_sign_extendhi:
+ case CODE_FOR_andsi3_sign_extendhi:
+ case CODE_FOR_iorsi3_sign_extendhi:
+ case CODE_FOR_xorsi3_sign_extendhi:
+ case CODE_FOR_divsi3_sign_extendhi:
+ case CODE_FOR_udivsi3_sign_extendhi:
+ case CODE_FOR_minussi3_sign_extendhi:
+ case CODE_FOR_smaxsi3_sign_extendhi:
+ case CODE_FOR_sminsi3_sign_extendhi:
+ case CODE_FOR_multsi3_sign_extendhi:
+ case CODE_FOR_comparesi3_sign_extendhi:
+ zero = false;
+ factor = 2;
+ break;
+
+ case CODE_FOR_plussi3_zero_extendqi:
+ case CODE_FOR_andsi3_zero_extendqi:
+ case CODE_FOR_iorsi3_zero_extendqi:
+ case CODE_FOR_xorsi3_zero_extendqi:
+ case CODE_FOR_divsi3_zero_extendqi:
+ case CODE_FOR_udivsi3_zero_extendqi:
+ case CODE_FOR_minussi3_zero_extendqi:
+ case CODE_FOR_smaxsi3_zero_extendqi:
+ case CODE_FOR_sminsi3_zero_extendqi:
+ case CODE_FOR_multsi3_zero_extendqi:
+ case CODE_FOR_comparesi3_zero_extendqi:
+ zero = true;
+ factor = 1;
+ break;
+
+ case CODE_FOR_plussi3_sign_extendqi:
+ case CODE_FOR_andsi3_sign_extendqi:
+ case CODE_FOR_iorsi3_sign_extendqi:
+ case CODE_FOR_xorsi3_sign_extendqi:
+ case CODE_FOR_divsi3_sign_extendqi:
+ case CODE_FOR_udivsi3_sign_extendqi:
+ case CODE_FOR_minussi3_sign_extendqi:
+ case CODE_FOR_smaxsi3_sign_extendqi:
+ case CODE_FOR_sminsi3_sign_extendqi:
+ case CODE_FOR_multsi3_sign_extendqi:
+ case CODE_FOR_comparesi3_sign_extendqi:
+ zero = false;
+ factor = 1;
+ break;
+ }
+
+ /* We are expecting: (SET (REG) (<OP> (REG) (<EXTEND> (MEM)))). */
+ extend = single_set (insn);
+ gcc_assert (extend != NULL_RTX);
+
+ extend = SET_SRC (extend);
+ if (GET_CODE (XEXP (extend, 0)) == ZERO_EXTEND
+ || GET_CODE (XEXP (extend, 0)) == SIGN_EXTEND)
+ extend = XEXP (extend, 0);
+ else
+ extend = XEXP (extend, 1);
+
+ gcc_assert ((zero && (GET_CODE (extend) == ZERO_EXTEND))
+ || (! zero && (GET_CODE (extend) == SIGN_EXTEND)));
+
+ mem = XEXP (extend, 0);
+ gcc_checking_assert (MEM_P (mem));
+ if (REG_P (XEXP (mem, 0)))
+ return (zero && factor == 1) ? 2 : 3;
+
+ /* We are expecting: (MEM (PLUS (REG) (CONST_INT))). */
+ gcc_checking_assert (GET_CODE (XEXP (mem, 0)) == PLUS);
+ gcc_checking_assert (REG_P (XEXP (XEXP (mem, 0), 0)));
+
+ offset = XEXP (XEXP (mem, 0), 1);
+ gcc_checking_assert (GET_CODE (offset) == CONST_INT);
+
+ if (IN_RANGE (INTVAL (offset), 0, 255 * factor))
+ return (zero && factor == 1) ? 3 : 4;
+
+ return (zero && factor == 1) ? 4 : 5;
+}
\f
+#undef TARGET_ASM_JUMP_ALIGN_MAX_SKIP
+#define TARGET_ASM_JUMP_ALIGN_MAX_SKIP rx_max_skip_for_label
+#undef TARGET_ASM_LOOP_ALIGN_MAX_SKIP
+#define TARGET_ASM_LOOP_ALIGN_MAX_SKIP rx_max_skip_for_label
+#undef TARGET_LABEL_ALIGN_AFTER_BARRIER_MAX_SKIP
+#define TARGET_LABEL_ALIGN_AFTER_BARRIER_MAX_SKIP rx_max_skip_for_label
+#undef TARGET_ASM_LABEL_ALIGN_MAX_SKIP
+#define TARGET_ASM_LABEL_ALIGN_MAX_SKIP rx_max_skip_for_label
+
#undef TARGET_FUNCTION_VALUE
#define TARGET_FUNCTION_VALUE rx_function_value
#undef TARGET_INIT_BUILTINS
#define TARGET_INIT_BUILTINS rx_init_builtins
+#undef TARGET_BUILTIN_DECL
+#define TARGET_BUILTIN_DECL rx_builtin_decl
+
#undef TARGET_EXPAND_BUILTIN
#define TARGET_EXPAND_BUILTIN rx_expand_builtin
#undef TARGET_LEGITIMATE_ADDRESS_P
#define TARGET_LEGITIMATE_ADDRESS_P rx_is_legitimate_address
+#undef TARGET_MODE_DEPENDENT_ADDRESS_P
+#define TARGET_MODE_DEPENDENT_ADDRESS_P rx_mode_dependent_address_p
+
#undef TARGET_ALLOCATE_STACK_SLOTS_FOR_ARGS
#define TARGET_ALLOCATE_STACK_SLOTS_FOR_ARGS rx_allocate_stack_slots_for_args
#undef TARGET_SET_CURRENT_FUNCTION
#define TARGET_SET_CURRENT_FUNCTION rx_set_current_function
-#undef TARGET_HANDLE_OPTION
-#define TARGET_HANDLE_OPTION rx_handle_option
-
#undef TARGET_ASM_INTEGER
#define TARGET_ASM_INTEGER rx_assemble_integer
#undef TARGET_OPTION_OVERRIDE
#define TARGET_OPTION_OVERRIDE rx_option_override
-#undef TARGET_OPTION_OPTIMIZATION_TABLE
-#define TARGET_OPTION_OPTIMIZATION_TABLE rx_option_optimization_table
-
#undef TARGET_PROMOTE_FUNCTION_MODE
#define TARGET_PROMOTE_FUNCTION_MODE rx_promote_function_mode
#undef TARGET_OVERRIDE_OPTIONS_AFTER_CHANGE
#define TARGET_OVERRIDE_OPTIONS_AFTER_CHANGE rx_override_options_after_change
-#undef TARGET_EXCEPT_UNWIND_INFO
-#define TARGET_EXCEPT_UNWIND_INFO sjlj_except_unwind_info
+#undef TARGET_FLAGS_REGNUM
+#define TARGET_FLAGS_REGNUM CC_REG
+
+#undef TARGET_LEGITIMATE_CONSTANT_P
+#define TARGET_LEGITIMATE_CONSTANT_P rx_is_legitimate_constant
+
+#undef TARGET_LEGITIMIZE_ADDRESS
+#define TARGET_LEGITIMIZE_ADDRESS rx_legitimize_address
struct gcc_target targetm = TARGET_INITIALIZER;
-/* #include "gt-rx.h" */
+#include "gt-rx.h"