/* Subroutines used for code generation on the Renesas M32R cpu.
- Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004
- Free Software Foundation, Inc.
+ Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
+ 2005 Free Software Foundation, Inc.
This file is part of GCC.
#include "recog.h"
#include "toplev.h"
#include "ggc.h"
+#include "integrate.h"
#include "tm_p.h"
#include "target.h"
#include "target-def.h"
tree, int *, int);
static void init_idents (void);
static bool m32r_rtx_costs (rtx, int, int, int *);
+static bool m32r_pass_by_reference (CUMULATIVE_ARGS *, enum machine_mode,
+ tree, bool);
+static int m32r_arg_partial_bytes (CUMULATIVE_ARGS *, enum machine_mode,
+ tree, bool);
\f
/* Initialize the GCC target structure. */
#undef TARGET_ATTRIBUTE_TABLE
#define TARGET_SCHED_ADJUST_PRIORITY m32r_adjust_priority
#undef TARGET_SCHED_ISSUE_RATE
#define TARGET_SCHED_ISSUE_RATE m32r_issue_rate
-#undef TARGET_SCHED_USE_DFA_PIPELINE_INTERFACE
-#define TARGET_SCHED_USE_DFA_PIPELINE_INTERFACE hook_int_void_1
#undef TARGET_ENCODE_SECTION_INFO
#define TARGET_ENCODE_SECTION_INFO m32r_encode_section_info
#undef TARGET_PROMOTE_PROTOTYPES
#define TARGET_PROMOTE_PROTOTYPES hook_bool_tree_true
-
#undef TARGET_RETURN_IN_MEMORY
#define TARGET_RETURN_IN_MEMORY m32r_return_in_memory
-
#undef TARGET_SETUP_INCOMING_VARARGS
#define TARGET_SETUP_INCOMING_VARARGS m32r_setup_incoming_varargs
+#undef TARGET_MUST_PASS_IN_STACK
+#define TARGET_MUST_PASS_IN_STACK must_pass_in_stack_var_size
+#undef TARGET_PASS_BY_REFERENCE
+#define TARGET_PASS_BY_REFERENCE m32r_pass_by_reference
+#undef TARGET_ARG_PARTIAL_BYTES
+#define TARGET_ARG_PARTIAL_BYTES m32r_arg_partial_bytes
struct gcc_target targetm = TARGET_INITIALIZER;
\f
&& arg != large_ident1
&& arg != large_ident2)
{
- warning ("invalid argument of `%s' attribute",
+ warning ("invalid argument of %qs attribute",
IDENTIFIER_POINTER (name));
*no_add_attrs = true;
}
to make it easy to experiment. */
}
\f
-/* Acceptable arguments to the call insn. */
-
-int
-call_address_operand (rtx op, enum machine_mode mode)
-{
- return symbolic_operand (op, mode);
-
-/* Constants and values in registers are not OK, because
- the m32r BL instruction can only support PC relative branching. */
-}
-
int
call_operand (rtx op, enum machine_mode mode)
{
return call_address_operand (op, mode);
}
-/* Returns 1 if OP is a symbol reference. */
-
-int
-symbolic_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
-{
- switch (GET_CODE (op))
- {
- case SYMBOL_REF:
- case LABEL_REF:
- case CONST :
- return 1;
-
- default:
- return 0;
- }
-}
-
/* Return 1 if OP is a reference to an object in .sdata/.sbss. */
int
return TARGET_CALL26;
}
-/* Returns 1 if OP is an acceptable operand for seth/add3. */
-
-int
-seth_add3_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
-{
- if (flag_pic)
- return 0;
-
- if (GET_CODE (op) == SYMBOL_REF
- || GET_CODE (op) == LABEL_REF)
- return 1;
-
- if (GET_CODE (op) == CONST
- && GET_CODE (XEXP (op, 0)) == PLUS
- && GET_CODE (XEXP (XEXP (op, 0), 0)) == SYMBOL_REF
- && GET_CODE (XEXP (XEXP (op, 0), 1)) == CONST_INT
- && INT16_P (INTVAL (XEXP (XEXP (op, 0), 1))))
- return 1;
-
- return 0;
-}
-
-/* Return true if OP is a signed 8 bit immediate value. */
-
-int
-int8_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
-{
- if (GET_CODE (op) != CONST_INT)
- return 0;
- return INT8_P (INTVAL (op));
-}
-
-/* Return true if OP is a signed 16 bit immediate value
- useful in comparisons. */
-
-int
-cmp_int16_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
-{
- if (GET_CODE (op) != CONST_INT)
- return 0;
- return CMP_INT16_P (INTVAL (op));
-}
-
-/* Return true if OP is an unsigned 16 bit immediate value. */
-
-int
-uint16_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
-{
- if (GET_CODE (op) != CONST_INT)
- return 0;
- return UINT16_P (INTVAL (op));
-}
-
-/* Return true if OP is a register or signed 16 bit value. */
-
-int
-reg_or_int16_operand (rtx op, enum machine_mode mode)
-{
- if (GET_CODE (op) == REG || GET_CODE (op) == SUBREG)
- return register_operand (op, mode);
- if (GET_CODE (op) != CONST_INT)
- return 0;
- return INT16_P (INTVAL (op));
-}
-
-/* Return true if OP is a register or an unsigned 16 bit value. */
-
-int
-reg_or_uint16_operand (rtx op, enum machine_mode mode)
-{
- if (GET_CODE (op) == REG || GET_CODE (op) == SUBREG)
- return register_operand (op, mode);
- if (GET_CODE (op) != CONST_INT)
- return 0;
- return UINT16_P (INTVAL (op));
-}
-
-/* Return true if OP is a register or an integer value that can be
- used is SEQ/SNE. We can use either XOR of the value or ADD of
- the negative of the value for the constant. Don't allow 0,
- because that is special cased. */
-
-int
-reg_or_eq_int16_operand (rtx op, enum machine_mode mode)
-{
- HOST_WIDE_INT value;
-
- if (GET_CODE (op) == REG || GET_CODE (op) == SUBREG)
- return register_operand (op, mode);
-
- if (GET_CODE (op) != CONST_INT)
- return 0;
-
- value = INTVAL (op);
- return (value != 0) && (UINT16_P (value) || CMP_INT16_P (-value));
-}
-
-/* Return true if OP is a register or signed 16 bit value for compares. */
-
-int
-reg_or_cmp_int16_operand (rtx op, enum machine_mode mode)
-{
- if (GET_CODE (op) == REG || GET_CODE (op) == SUBREG)
- return register_operand (op, mode);
- if (GET_CODE (op) != CONST_INT)
- return 0;
- return CMP_INT16_P (INTVAL (op));
-}
-
-/* Return true if OP is a register or the constant 0. */
-
-int
-reg_or_zero_operand (rtx op, enum machine_mode mode)
-{
- if (GET_CODE (op) == REG || GET_CODE (op) == SUBREG)
- return register_operand (op, mode);
-
- if (GET_CODE (op) != CONST_INT)
- return 0;
-
- return INTVAL (op) == 0;
-}
-
-/* Return true if OP is a const_int requiring two instructions to load. */
-
-int
-two_insn_const_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
-{
- if (GET_CODE (op) != CONST_INT)
- return 0;
- if (INT16_P (INTVAL (op))
- || UINT24_P (INTVAL (op))
- || UPPER16_P (INTVAL (op)))
- return 0;
- return 1;
-}
-
-/* Return true if OP is an acceptable argument for a single word
- move source. */
-
-int
-move_src_operand (rtx op, enum machine_mode mode)
-{
- switch (GET_CODE (op))
- {
- case LABEL_REF :
- case SYMBOL_REF :
- case CONST :
- return addr24_operand (op, mode);
- case CONST_INT :
- /* ??? We allow more cse opportunities if we only allow constants
- loadable with one insn, and split the rest into two. The instances
- where this would help should be rare and the current way is
- simpler. */
- if (HOST_BITS_PER_WIDE_INT > 32)
- {
- HOST_WIDE_INT rest = INTVAL (op) >> 31;
- return (rest == 0 || rest == -1);
- }
- else
- return 1;
- case CONST_DOUBLE :
- if (mode == SFmode)
- return 1;
- else if (mode == SImode)
- {
- /* Large unsigned constants are represented as const_double's. */
- unsigned HOST_WIDE_INT low, high;
-
- low = CONST_DOUBLE_LOW (op);
- high = CONST_DOUBLE_HIGH (op);
- return high == 0 && low <= (unsigned) 0xffffffff;
- }
- else
- return 0;
- case REG :
- return register_operand (op, mode);
- case SUBREG :
- /* (subreg (mem ...) ...) can occur here if the inner part was once a
- pseudo-reg and is now a stack slot. */
- if (GET_CODE (SUBREG_REG (op)) == MEM)
- return address_operand (XEXP (SUBREG_REG (op), 0), mode);
- else
- return register_operand (op, mode);
- case MEM :
- if (GET_CODE (XEXP (op, 0)) == PRE_INC
- || GET_CODE (XEXP (op, 0)) == PRE_DEC)
- return 0; /* loads can't do pre-{inc,dec} */
- return address_operand (XEXP (op, 0), mode);
- default :
- return 0;
- }
-}
-
-/* Return true if OP is an acceptable argument for a double word
- move source. */
-
-int
-move_double_src_operand (rtx op, enum machine_mode mode)
-{
- switch (GET_CODE (op))
- {
- case CONST_INT :
- case CONST_DOUBLE :
- return 1;
- case REG :
- return register_operand (op, mode);
- case SUBREG :
- /* (subreg (mem ...) ...) can occur here if the inner part was once a
- pseudo-reg and is now a stack slot. */
- if (GET_CODE (SUBREG_REG (op)) == MEM)
- return move_double_src_operand (SUBREG_REG (op), mode);
- else
- return register_operand (op, mode);
- case MEM :
- /* Disallow auto inc/dec for now. */
- if (GET_CODE (XEXP (op, 0)) == PRE_DEC
- || GET_CODE (XEXP (op, 0)) == PRE_INC)
- return 0;
- return address_operand (XEXP (op, 0), mode);
- default :
- return 0;
- }
-}
-
-/* Return true if OP is an acceptable argument for a move destination. */
-
-int
-move_dest_operand (rtx op, enum machine_mode mode)
-{
- switch (GET_CODE (op))
- {
- case REG :
- return register_operand (op, mode);
- case SUBREG :
- /* (subreg (mem ...) ...) can occur here if the inner part was once a
- pseudo-reg and is now a stack slot. */
- if (GET_CODE (SUBREG_REG (op)) == MEM)
- return address_operand (XEXP (SUBREG_REG (op), 0), mode);
- else
- return register_operand (op, mode);
- case MEM :
- if (GET_CODE (XEXP (op, 0)) == POST_INC)
- return 0; /* stores can't do post inc */
- return address_operand (XEXP (op, 0), mode);
- default :
- return 0;
- }
-}
-
/* Return 1 if OP is a DImode const we want to handle inline.
This must match the code in the movdi pattern.
It is used by the 'G' CONST_DOUBLE_OK_FOR_LETTER. */
return 0;
}
-/* Return 1 if OP is an EQ or NE comparison operator. */
-
-int
-eqne_comparison_operator (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
-{
- enum rtx_code code = GET_CODE (op);
-
- return (code == EQ || code == NE);
-}
-
-/* Return 1 if OP is a signed comparison operator. */
-
-int
-signed_comparison_operator (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
-{
- enum rtx_code code = GET_CODE (op);
-
- return (COMPARISON_P (op)
- && (code == EQ || code == NE
- || code == LT || code == LE || code == GT || code == GE));
-}
-
/* Return 1 if OP is (mem (reg ...)).
This is used in insn length calcs. */
return GET_CODE (op) == MEM && GET_CODE (XEXP (op, 0)) == REG;
}
-/* Return true if OP is an acceptable input argument for a zero/sign extend
- operation. */
-
-int
-extend_operand (rtx op, enum machine_mode mode)
-{
- rtx addr;
-
- switch (GET_CODE (op))
- {
- case REG :
- case SUBREG :
- return register_operand (op, mode);
-
- case MEM :
- addr = XEXP (op, 0);
- if (GET_CODE (addr) == PRE_INC || GET_CODE (addr) == PRE_DEC)
- return 0; /* loads can't do pre inc/pre dec */
-
- return address_operand (addr, mode);
+/* Return nonzero if TYPE must be passed by indirect reference. */
- default :
- return 0;
- }
-}
-
-/* Return nonzero if the operand is an insn that is a small insn.
- Allow const_int 0 as well, which is a placeholder for NOP slots. */
-
-int
-small_insn_p (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
-{
- if (GET_CODE (op) == CONST_INT && INTVAL (op) == 0)
- return 1;
-
- if (! INSN_P (op))
- return 0;
-
- return get_attr_length (op) == 2;
-}
-
-/* Return nonzero if the operand is an insn that is a large insn. */
-
-int
-large_insn_p (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
-{
- if (! INSN_P (op))
- return 0;
-
- return get_attr_length (op) != 2;
-}
-
-/* Return nonzero if TYPE must be passed or returned in memory.
- The m32r treats both directions the same so we handle both directions
- in this function. */
-
-int
-m32r_pass_by_reference (tree type)
+static bool
+m32r_pass_by_reference (CUMULATIVE_ARGS *ca ATTRIBUTE_UNUSED,
+ enum machine_mode mode, tree type,
+ bool named ATTRIBUTE_UNUSED)
{
- int size = int_size_in_bytes (type);
+ int size;
- if (size < 0 || size > 8)
- return 1;
+ if (type)
+ size = int_size_in_bytes (type);
+ else
+ size = GET_MODE_SIZE (mode);
- return 0;
+ return (size < 0 || size > 8);
}
\f
/* Comparisons. */
y = force_reg (GET_MODE (x), y);
else
{
- int ok_const =
- (code == LTU || code == LEU || code == GTU || code == GEU)
- ? uint16_operand (y, GET_MODE (y))
- : reg_or_cmp_int16_operand (y, GET_MODE (y));
-
+ int ok_const = reg_or_int16_operand (y, GET_MODE (y));
+
if (! ok_const)
y = force_reg (GET_MODE (x), y);
}
}
\f
-/* Implements the FUNCTION_ARG_PARTIAL_NREGS macro. */
-
-int
-function_arg_partial_nregs (CUMULATIVE_ARGS *cum, enum machine_mode mode,
- tree type, int named ATTRIBUTE_UNUSED)
+static int
+m32r_arg_partial_bytes (CUMULATIVE_ARGS *cum, enum machine_mode mode,
+ tree type, bool named ATTRIBUTE_UNUSED)
{
- int ret;
+ int words;
unsigned int size =
(((mode == BLKmode && type)
? (unsigned int) int_size_in_bytes (type)
/ UNITS_PER_WORD;
if (*cum >= M32R_MAX_PARM_REGS)
- ret = 0;
+ words = 0;
else if (*cum + size > M32R_MAX_PARM_REGS)
- ret = (*cum + size) - M32R_MAX_PARM_REGS;
+ words = (*cum + size) - M32R_MAX_PARM_REGS;
else
- ret = 0;
+ words = 0;
- return ret;
+ return words * UNITS_PER_WORD;
}
/* Worker function for TARGET_RETURN_IN_MEMORY. */
static bool
m32r_return_in_memory (tree type, tree fntype ATTRIBUTE_UNUSED)
{
- return m32r_pass_by_reference (type);
+ return m32r_pass_by_reference (NULL, TYPE_MODE (type), type, false);
}
/* Do any needed setup for a variadic function. For the M32R, we must
}
\f
-/* Implement `va_arg'. */
-
-rtx
-m32r_va_arg (tree valist, tree type)
-{
- HOST_WIDE_INT size, rsize;
- tree t;
- rtx addr_rtx;
-
- size = int_size_in_bytes (type);
- rsize = (size + UNITS_PER_WORD - 1) & -UNITS_PER_WORD;
-
- if (m32r_pass_by_reference (type))
- {
- tree type_ptr, type_ptr_ptr;
-
- /* Pass by reference. */
- type_ptr = build_pointer_type (type);
- type_ptr_ptr = build_pointer_type (type_ptr);
-
- t = build (POSTINCREMENT_EXPR, va_list_type_node, valist,
- build_int_2 (UNITS_PER_WORD, 0));
- TREE_SIDE_EFFECTS (t) = 1;
- t = build1 (NOP_EXPR, type_ptr_ptr, t);
- TREE_SIDE_EFFECTS (t) = 1;
- t = build1 (INDIRECT_REF, type_ptr, t);
-
- addr_rtx = expand_expr (t, NULL_RTX, Pmode, EXPAND_NORMAL);
- }
- else
- {
- /* Pass by value. */
- if (size < UNITS_PER_WORD)
- {
- /* Care for bigendian correction on the aligned address. */
- t = build (PLUS_EXPR, ptr_type_node, valist,
- build_int_2 (rsize - size, 0));
- addr_rtx = expand_expr (t, NULL_RTX, Pmode, EXPAND_NORMAL);
- addr_rtx = copy_to_reg (addr_rtx);
-
- /* Increment AP. */
- t = build (PLUS_EXPR, va_list_type_node, valist,
- build_int_2 (rsize, 0));
- t = build (MODIFY_EXPR, va_list_type_node, valist, t);
- TREE_SIDE_EFFECTS (t) = 1;
- expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
- }
- else
- {
- t = build (POSTINCREMENT_EXPR, va_list_type_node, valist,
- build_int_2 (rsize, 0));
- TREE_SIDE_EFFECTS (t) = 1;
- addr_rtx = expand_expr (t, NULL_RTX, Pmode, EXPAND_NORMAL);
- }
- }
-
- return addr_rtx;
-}
-\f
/* Return true if INSN is real instruction bearing insn. */
static int
The return address and frame pointer are treated separately.
Don't consider them here. */
#define MUST_SAVE_REGISTER(regno, interrupt_p) \
-((regno) != RETURN_ADDR_REGNUM && (regno) != FRAME_POINTER_REGNUM \
- && (regs_ever_live[regno] && (!call_used_regs[regno] || interrupt_p)))
+ ((regno) != RETURN_ADDR_REGNUM && (regno) != FRAME_POINTER_REGNUM \
+ && (regs_ever_live[regno] && (!call_really_used_regs[regno] || interrupt_p)))
#define MUST_SAVE_FRAME_POINTER (regs_ever_live[FRAME_POINTER_REGNUM])
#define MUST_SAVE_RETURN_ADDR (regs_ever_live[RETURN_ADDR_REGNUM] || current_function_profile)
else
address = reg;
+ current_function_uses_pic_offset_table = 1;
+
+ if (GET_CODE (orig) == LABEL_REF
+ || (GET_CODE (orig) == SYMBOL_REF && SYMBOL_REF_LOCAL_P (orig)))
+ {
+ emit_insn (gen_gotoff_load_addr (reg, orig));
+ emit_insn (gen_addsi3 (reg, reg, pic_offset_table_rtx));
+ return reg;
+ }
+
emit_insn (gen_pic_load_addr (address, orig));
emit_insn (gen_addsi3 (address, address, pic_offset_table_rtx));
- pic_ref = gen_rtx_MEM (Pmode, address);
-
- RTX_UNCHANGING_P (pic_ref) = 1;
+ pic_ref = gen_const_mem (Pmode, address);
insn = emit_move_insn (reg, pic_ref);
- current_function_uses_pic_offset_table = 1;
#if 0
/* Put a REG_EQUAL note on this insn, so that it can be optimized
by loop. */
||((INTVAL (operand1) == 1) && (INTVAL (operand2) == 0)));
}
-/* Return nonzero if the operand is suitable for use in a conditional move sequence. */
-
-int
-conditional_move_operand (rtx operand, enum machine_mode mode)
-{
- /* Only defined for simple integers so far... */
- if (mode != SImode && mode != HImode && mode != QImode)
- return FALSE;
-
- /* At the moment we can handle moving registers and loading constants. */
- /* To be added: Addition/subtraction/bitops/multiplication of registers. */
-
- switch (GET_CODE (operand))
- {
- case REG:
- return 1;
-
- case CONST_INT:
- return INT8_P (INTVAL (operand));
-
- default:
-#if 0
- fprintf (stderr, "Test for cond move op of type: %s\n",
- GET_RTX_NAME (GET_CODE (operand)));
-#endif
- return 0;
- }
-}
-
-/* Return true if the code is a test of the carry bit. */
-
-int
-carry_compare_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
-{
- rtx x;
-
- if (GET_MODE (op) != CCmode && GET_MODE (op) != VOIDmode)
- return FALSE;
-
- if (GET_CODE (op) != NE && GET_CODE (op) != EQ)
- return FALSE;
-
- x = XEXP (op, 0);
- if (GET_CODE (x) != REG || REGNO (x) != CARRY_REGNUM)
- return FALSE;
-
- x = XEXP (op, 1);
- if (GET_CODE (x) != CONST_INT || INTVAL (x) != 0)
- return FALSE;
-
- return TRUE;
-}
-
/* Generate the correct assembler code to handle the conditional loading of a
value into a register. It is known that the operands satisfy the
conditional_move_operand() function above. The destination is operand[0].
TYPE_MODE (sizetype));
}
-/* The maximum number of bytes to copy using pairs of load/store instructions.
- If a block is larger than this then a loop will be generated to copy
- MAX_MOVE_BYTES chunks at a time. The value of 32 is a semi-arbitrary choice.
- A customer uses Dhrystome as their benchmark, and Dhrystone has a 31 byte
- string copy in it. */
-#define MAX_MOVE_BYTES 32
-
/* Expand string/block move operations.
operands[0] is the pointer to the destination.
to the word after the end of the source block, and dst_reg to point
to the last word of the destination block, provided that the block
is MAX_MOVE_BYTES long. */
- emit_insn (gen_movstrsi_internal (dst_reg, src_reg, at_a_time,
+ emit_insn (gen_movmemsi_internal (dst_reg, src_reg, at_a_time,
new_dst_reg, new_src_reg));
emit_move_insn (dst_reg, new_dst_reg);
emit_move_insn (src_reg, new_src_reg);
}
if (leftover)
- emit_insn (gen_movstrsi_internal (dst_reg, src_reg, GEN_INT (leftover),
+ emit_insn (gen_movmemsi_internal (dst_reg, src_reg, GEN_INT (leftover),
gen_reg_rtx (SImode),
gen_reg_rtx (SImode)));
}
}
}
-/* Return true if op is an integer constant, less than or equal to
- MAX_MOVE_BYTES. */
-
-int
-m32r_block_immediate_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
-{
- if (GET_CODE (op) != CONST_INT
- || INTVAL (op) > MAX_MOVE_BYTES
- || INTVAL (op) <= 0)
- return 0;
-
- return 1;
-}
-
/* Return true if using NEW_REG in place of OLD_REG is ok. */
int