| Index: gcc/gcc/config/iq2000/iq2000.c
|
| diff --git a/gcc/gcc/config/iq2000/iq2000.c b/gcc/gcc/config/iq2000/iq2000.c
|
| index 71533894551f7915aa090d733f71c229e5b752ad..4359f5edfb93cb135dd0120e5f946b1d76d1684b 100644
|
| --- a/gcc/gcc/config/iq2000/iq2000.c
|
| +++ b/gcc/gcc/config/iq2000/iq2000.c
|
| @@ -1,5 +1,5 @@
|
| /* Subroutines used for code generation on Vitesse IQ2000 processors
|
| - Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008
|
| + Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009
|
| Free Software Foundation, Inc.
|
|
|
| This file is part of GCC.
|
| @@ -89,7 +89,7 @@ struct iq2000_frame_info
|
| int num_gp; /* Number of gp registers saved. */
|
| } iq2000_frame_info;
|
|
|
| -struct machine_function GTY(())
|
| +struct GTY(()) machine_function
|
| {
|
| /* Current frame information, calculated by compute_frame_size. */
|
| long total_size; /* # bytes that the entire frame takes up. */
|
| @@ -118,13 +118,6 @@ enum processor_type iq2000_tune;
|
| /* Which instruction set architecture to use. */
|
| int iq2000_isa;
|
|
|
| -/* Cached operands, and operator to compare for use in set/branch/trap
|
| - on condition codes. */
|
| -rtx branch_cmp[2];
|
| -
|
| -/* What type of branch to use. */
|
| -enum cmp_type branch_type;
|
| -
|
| /* Local variables. */
|
|
|
| /* The next branch instruction is a branch likely, not branch normal. */
|
| @@ -165,11 +158,18 @@ static void iq2000_setup_incoming_varargs (CUMULATIVE_ARGS *,
|
| static bool iq2000_rtx_costs (rtx, int, int, int *, bool);
|
| static int iq2000_address_cost (rtx, bool);
|
| static section *iq2000_select_section (tree, int, unsigned HOST_WIDE_INT);
|
| +static rtx iq2000_legitimize_address (rtx, rtx, enum machine_mode);
|
| static bool iq2000_pass_by_reference (CUMULATIVE_ARGS *, enum machine_mode,
|
| const_tree, bool);
|
| static int iq2000_arg_partial_bytes (CUMULATIVE_ARGS *, enum machine_mode,
|
| tree, bool);
|
| static void iq2000_va_start (tree, rtx);
|
| +static bool iq2000_legitimate_address_p (enum machine_mode, rtx, bool);
|
| +static bool iq2000_can_eliminate (const int, const int);
|
| +static void iq2000_asm_trampoline_template (FILE *);
|
| +static void iq2000_trampoline_init (rtx, tree, rtx);
|
| +static rtx iq2000_function_value (const_tree, const_tree, bool);
|
| +static rtx iq2000_libcall_value (enum machine_mode, const_rtx);
|
|
|
| #undef TARGET_INIT_BUILTINS
|
| #define TARGET_INIT_BUILTINS iq2000_init_builtins
|
| @@ -186,18 +186,23 @@ static void iq2000_va_start (tree, rtx);
|
| #undef TARGET_ASM_SELECT_SECTION
|
| #define TARGET_ASM_SELECT_SECTION iq2000_select_section
|
|
|
| +#undef TARGET_LEGITIMIZE_ADDRESS
|
| +#define TARGET_LEGITIMIZE_ADDRESS iq2000_legitimize_address
|
| +
|
| /* The assembler supports switchable .bss sections, but
|
| iq2000_select_section doesn't yet make use of them. */
|
| #undef TARGET_HAVE_SWITCHABLE_BSS_SECTIONS
|
| #define TARGET_HAVE_SWITCHABLE_BSS_SECTIONS false
|
|
|
| -#undef TARGET_PROMOTE_FUNCTION_ARGS
|
| -#define TARGET_PROMOTE_FUNCTION_ARGS hook_bool_const_tree_true
|
| -#undef TARGET_PROMOTE_FUNCTION_RETURN
|
| -#define TARGET_PROMOTE_FUNCTION_RETURN hook_bool_const_tree_true
|
| +#undef TARGET_PROMOTE_FUNCTION_MODE
|
| +#define TARGET_PROMOTE_FUNCTION_MODE default_promote_function_mode_always_promote
|
| #undef TARGET_PROMOTE_PROTOTYPES
|
| #define TARGET_PROMOTE_PROTOTYPES hook_bool_const_tree_true
|
|
|
| +#undef TARGET_FUNCTION_VALUE
|
| +#define TARGET_FUNCTION_VALUE iq2000_function_value
|
| +#undef TARGET_LIBCALL_VALUE
|
| +#define TARGET_LIBCALL_VALUE iq2000_libcall_value
|
| #undef TARGET_RETURN_IN_MEMORY
|
| #define TARGET_RETURN_IN_MEMORY iq2000_return_in_memory
|
| #undef TARGET_PASS_BY_REFERENCE
|
| @@ -215,6 +220,17 @@ static void iq2000_va_start (tree, rtx);
|
| #undef TARGET_EXPAND_BUILTIN_VA_START
|
| #define TARGET_EXPAND_BUILTIN_VA_START iq2000_va_start
|
|
|
| +#undef TARGET_LEGITIMATE_ADDRESS_P
|
| +#define TARGET_LEGITIMATE_ADDRESS_P iq2000_legitimate_address_p
|
| +
|
| +#undef TARGET_CAN_ELIMINATE
|
| +#define TARGET_CAN_ELIMINATE iq2000_can_eliminate
|
| +
|
| +#undef TARGET_ASM_TRAMPOLINE_TEMPLATE
|
| +#define TARGET_ASM_TRAMPOLINE_TEMPLATE iq2000_asm_trampoline_template
|
| +#undef TARGET_TRAMPOLINE_INIT
|
| +#define TARGET_TRAMPOLINE_INIT iq2000_trampoline_init
|
| +
|
| struct gcc_target targetm = TARGET_INITIALIZER;
|
|
|
| /* Return nonzero if we split the address into high and low parts. */
|
| @@ -252,12 +268,12 @@ iq2000_reg_mode_ok_for_base_p (rtx reg,
|
| memory operand of the indicated MODE. STRICT is nonzero if this
|
| function is called during reload. */
|
|
|
| -int
|
| -iq2000_legitimate_address_p (enum machine_mode mode, rtx xinsn, int strict)
|
| +bool
|
| +iq2000_legitimate_address_p (enum machine_mode mode, rtx xinsn, bool strict)
|
| {
|
| if (TARGET_DEBUG_A_MODE)
|
| {
|
| - GO_PRINTF2 ("\n========== GO_IF_LEGITIMATE_ADDRESS, %sstrict\n",
|
| + GO_PRINTF2 ("\n========== legitimate_address_p, %sstrict\n",
|
| strict ? "" : "not ");
|
| GO_DEBUG_RTX (xinsn);
|
| }
|
| @@ -314,7 +330,7 @@ iq2000_legitimate_address_p (enum machine_mode mode, rtx xinsn, int strict)
|
| }
|
|
|
| if (TARGET_DEBUG_A_MODE)
|
| - GO_PRINTF ("Not a legitimate address\n");
|
| + GO_PRINTF ("Not a enum machine_mode mode, legitimate address\n");
|
|
|
| /* The address was not legitimate. */
|
| return 0;
|
| @@ -1006,60 +1022,31 @@ gen_int_relational (enum rtx_code test_code, rtx result, rtx cmp0, rtx cmp1,
|
| The comparison operands are saved away by cmp{si,di,sf,df}. */
|
|
|
| void
|
| -gen_conditional_branch (rtx operands[], enum rtx_code test_code)
|
| +gen_conditional_branch (rtx operands[], enum machine_mode mode)
|
| {
|
| - enum cmp_type type = branch_type;
|
| - rtx cmp0 = branch_cmp[0];
|
| - rtx cmp1 = branch_cmp[1];
|
| - enum machine_mode mode;
|
| + enum rtx_code test_code = GET_CODE (operands[0]);
|
| + rtx cmp0 = operands[1];
|
| + rtx cmp1 = operands[2];
|
| rtx reg;
|
| int invert;
|
| rtx label1, label2;
|
|
|
| - switch (type)
|
| - {
|
| - case CMP_SI:
|
| - case CMP_DI:
|
| - mode = type == CMP_SI ? SImode : DImode;
|
| - invert = 0;
|
| - reg = gen_int_relational (test_code, NULL_RTX, cmp0, cmp1, &invert);
|
| -
|
| - if (reg)
|
| - {
|
| - cmp0 = reg;
|
| - cmp1 = const0_rtx;
|
| - test_code = NE;
|
| - }
|
| - else if (GET_CODE (cmp1) == CONST_INT && INTVAL (cmp1) != 0)
|
| - /* We don't want to build a comparison against a nonzero
|
| - constant. */
|
| - cmp1 = force_reg (mode, cmp1);
|
| -
|
| - break;
|
| -
|
| - case CMP_SF:
|
| - case CMP_DF:
|
| - reg = gen_reg_rtx (CCmode);
|
| -
|
| - /* For cmp0 != cmp1, build cmp0 == cmp1, and test for result == 0. */
|
| - emit_insn (gen_rtx_SET (VOIDmode, reg,
|
| - gen_rtx_fmt_ee (test_code == NE ? EQ : test_code,
|
| - CCmode, cmp0, cmp1)));
|
| + invert = 0;
|
| + reg = gen_int_relational (test_code, NULL_RTX, cmp0, cmp1, &invert);
|
|
|
| - test_code = test_code == NE ? EQ : NE;
|
| - mode = CCmode;
|
| + if (reg)
|
| + {
|
| cmp0 = reg;
|
| cmp1 = const0_rtx;
|
| - invert = 0;
|
| - break;
|
| -
|
| - default:
|
| - abort_with_insn (gen_rtx_fmt_ee (test_code, VOIDmode, cmp0, cmp1),
|
| - "bad test");
|
| + test_code = NE;
|
| }
|
| + else if (GET_CODE (cmp1) == CONST_INT && INTVAL (cmp1) != 0)
|
| + /* We don't want to build a comparison against a nonzero
|
| + constant. */
|
| + cmp1 = force_reg (mode, cmp1);
|
|
|
| /* Generate the branch. */
|
| - label1 = gen_rtx_LABEL_REF (VOIDmode, operands[0]);
|
| + label1 = gen_rtx_LABEL_REF (VOIDmode, operands[3]);
|
| label2 = pc_rtx;
|
|
|
| if (invert)
|
| @@ -1175,6 +1162,11 @@ function_arg_advance (CUMULATIVE_ARGS *cum, enum machine_mode mode, tree type,
|
| cum->arg_words += 2;
|
| break;
|
|
|
| + case TImode:
|
| + cum->gp_reg_found = 1;
|
| + cum->arg_words += 4;
|
| + break;
|
| +
|
| case QImode:
|
| case HImode:
|
| case SImode:
|
| @@ -1245,6 +1237,12 @@ function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode, const_tree type,
|
| case DImode:
|
| cum->arg_words += (cum->arg_words & 1);
|
| regbase = GP_ARG_FIRST;
|
| + break;
|
| +
|
| + case TImode:
|
| + cum->arg_words += (cum->arg_words & 3);
|
| + regbase = GP_ARG_FIRST;
|
| + break;
|
| }
|
|
|
| if (*arg_words >= (unsigned) MAX_ARGS_IN_REGISTERS)
|
| @@ -1697,6 +1695,22 @@ compute_frame_size (HOST_WIDE_INT size)
|
| return total_size;
|
| }
|
|
|
| +
|
| +/* We can always eliminate to the frame pointer. We can eliminate to the
|
| + stack pointer unless a frame pointer is needed. */
|
| +
|
| +bool
|
| +iq2000_can_eliminate (const int from, const int to)
|
| +{
|
| + return (from == RETURN_ADDRESS_POINTER_REGNUM
|
| + && (! leaf_function_p ()
|
| + || (to == GP_REG_FIRST + 31 && leaf_function_p)))
|
| + || (from != RETURN_ADDRESS_POINTER_REGNUM
|
| + && (to == HARD_FRAME_POINTER_REGNUM
|
| + || (to == STACK_POINTER_REGNUM
|
| + && ! frame_pointer_needed)));
|
| +}
|
| +
|
| /* Implement INITIAL_ELIMINATION_OFFSET. FROM is either the frame
|
| pointer, argument pointer, or return address pointer. TO is either
|
| the stack pointer or hard frame pointer. */
|
| @@ -1877,7 +1891,8 @@ iq2000_expand_prologue (void)
|
| && targetm.calls.struct_value_rtx (TREE_TYPE (fndecl), 1) == 0)
|
| {
|
| tree type = build_pointer_type (fntype);
|
| - tree function_result_decl = build_decl (PARM_DECL, NULL_TREE, type);
|
| + tree function_result_decl = build_decl (BUILTINS_LOCATION,
|
| + PARM_DECL, NULL_TREE, type);
|
|
|
| DECL_ARG_TYPE (function_result_decl) = type;
|
| TREE_CHAIN (function_result_decl) = fnargs;
|
| @@ -2201,19 +2216,47 @@ iq2000_select_section (tree decl, int reloc ATTRIBUTE_UNUSED,
|
| /* Return register to use for a function return value with VALTYPE for function
|
| FUNC. */
|
|
|
| -rtx
|
| -iq2000_function_value (const_tree valtype, const_tree func ATTRIBUTE_UNUSED)
|
| +static rtx
|
| +iq2000_function_value (const_tree valtype,
|
| + const_tree fn_decl_or_type,
|
| + bool outgoing ATTRIBUTE_UNUSED)
|
| {
|
| int reg = GP_RETURN;
|
| enum machine_mode mode = TYPE_MODE (valtype);
|
| int unsignedp = TYPE_UNSIGNED (valtype);
|
| + tree func = fn_decl_or_type;
|
| +
|
| + if (fn_decl_or_type
|
| + && !DECL_P (fn_decl_or_type))
|
| + fn_decl_or_type = NULL;
|
|
|
| - /* Since we define TARGET_PROMOTE_FUNCTION_RETURN that returns true,
|
| - we must promote the mode just as PROMOTE_MODE does. */
|
| - mode = promote_mode (valtype, mode, &unsignedp, 1);
|
| + /* Since we promote return types, we must promote the mode here too. */
|
| + mode = promote_function_mode (valtype, mode, &unsignedp, func, 1);
|
|
|
| return gen_rtx_REG (mode, reg);
|
| }
|
| +
|
| +/* Worker function for TARGET_LIBCALL_VALUE. */
|
| +
|
| +static rtx
|
| +iq2000_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) >= 4)
|
| + ? mode : SImode),
|
| + GP_RETURN);
|
| +}
|
| +
|
| +/* Worker function for FUNCTION_VALUE_REGNO_P.
|
| +
|
| + On the IQ2000, R2 and R3 are the only register thus used. */
|
| +
|
| +bool
|
| +iq2000_function_value_regno_p (const unsigned int regno)
|
| +{
|
| + return (regno == GP_RETURN);
|
| +}
|
| +
|
|
|
| /* Return true when an argument must be passed by reference. */
|
|
|
| @@ -3203,6 +3246,73 @@ print_operand (FILE *file, rtx op, int letter)
|
| output_addr_const (file, op);
|
| }
|
|
|
| +
|
| +/* For the IQ2000, transform:
|
| +
|
| + memory(X + <large int>)
|
| + into:
|
| + Y = <large int> & ~0x7fff;
|
| + Z = X + Y
|
| + memory (Z + (<large int> & 0x7fff));
|
| +*/
|
| +
|
| +rtx
|
| +iq2000_legitimize_address (rtx xinsn, rtx old_x ATTRIBUTE_UNUSED,
|
| + enum machine_mode mode)
|
| +{
|
| + if (TARGET_DEBUG_B_MODE)
|
| + {
|
| + GO_PRINTF ("\n========== LEGITIMIZE_ADDRESS\n");
|
| + GO_DEBUG_RTX (xinsn);
|
| + }
|
| +
|
| + if (iq2000_check_split (xinsn, mode))
|
| + {
|
| + return gen_rtx_LO_SUM (Pmode,
|
| + copy_to_mode_reg (Pmode,
|
| + gen_rtx_HIGH (Pmode, xinsn)),
|
| + xinsn);
|
| + }
|
| +
|
| + if (GET_CODE (xinsn) == PLUS)
|
| + {
|
| + rtx xplus0 = XEXP (xinsn, 0);
|
| + rtx xplus1 = XEXP (xinsn, 1);
|
| + enum rtx_code code0 = GET_CODE (xplus0);
|
| + enum rtx_code code1 = GET_CODE (xplus1);
|
| +
|
| + if (code0 != REG && code1 == REG)
|
| + {
|
| + xplus0 = XEXP (xinsn, 1);
|
| + xplus1 = XEXP (xinsn, 0);
|
| + code0 = GET_CODE (xplus0);
|
| + code1 = GET_CODE (xplus1);
|
| + }
|
| +
|
| + if (code0 == REG && REG_MODE_OK_FOR_BASE_P (xplus0, mode)
|
| + && code1 == CONST_INT && !SMALL_INT (xplus1))
|
| + {
|
| + rtx int_reg = gen_reg_rtx (Pmode);
|
| + rtx ptr_reg = gen_reg_rtx (Pmode);
|
| +
|
| + emit_move_insn (int_reg,
|
| + GEN_INT (INTVAL (xplus1) & ~ 0x7fff));
|
| +
|
| + emit_insn (gen_rtx_SET (VOIDmode,
|
| + ptr_reg,
|
| + gen_rtx_PLUS (Pmode, xplus0, int_reg)));
|
| +
|
| + return plus_constant (ptr_reg, INTVAL (xplus1) & 0x7fff);
|
| + }
|
| + }
|
| +
|
| + if (TARGET_DEBUG_B_MODE)
|
| + GO_PRINTF ("LEGITIMIZE_ADDRESS could not fix.\n");
|
| +
|
| + return xinsn;
|
| +}
|
| +
|
| +
|
| static bool
|
| iq2000_rtx_costs (rtx x, int code, int outer_code ATTRIBUTE_UNUSED, int * total,
|
| bool speed ATTRIBUTE_UNUSED)
|
| @@ -3342,4 +3452,47 @@ iq2000_rtx_costs (rtx x, int code, int outer_code ATTRIBUTE_UNUSED, int * total,
|
| return true;
|
| }
|
|
|
| +/* Worker for TARGET_ASM_TRAMPOLINE_TEMPLATE. */
|
| +
|
| +static void
|
| +iq2000_asm_trampoline_template (FILE *f)
|
| +{
|
| + fprintf (f, "\t.word\t0x03e00821\t\t# move $1,$31\n");
|
| + fprintf (f, "\t.word\t0x04110001\t\t# bgezal $0,.+8\n");
|
| + fprintf (f, "\t.word\t0x00000000\t\t# nop\n");
|
| + if (Pmode == DImode)
|
| + {
|
| + fprintf (f, "\t.word\t0xdfe30014\t\t# ld $3,20($31)\n");
|
| + fprintf (f, "\t.word\t0xdfe2001c\t\t# ld $2,28($31)\n");
|
| + }
|
| + else
|
| + {
|
| + fprintf (f, "\t.word\t0x8fe30014\t\t# lw $3,20($31)\n");
|
| + fprintf (f, "\t.word\t0x8fe20018\t\t# lw $2,24($31)\n");
|
| + }
|
| + fprintf (f, "\t.word\t0x0060c821\t\t# move $25,$3 (abicalls)\n");
|
| + fprintf (f, "\t.word\t0x00600008\t\t# jr $3\n");
|
| + fprintf (f, "\t.word\t0x0020f821\t\t# move $31,$1\n");
|
| + fprintf (f, "\t.word\t0x00000000\t\t# <function address>\n");
|
| + fprintf (f, "\t.word\t0x00000000\t\t# <static chain value>\n");
|
| +}
|
| +
|
| +/* Worker for TARGET_TRAMPOLINE_INIT. */
|
| +
|
| +static void
|
| +iq2000_trampoline_init (rtx m_tramp, tree fndecl, rtx chain_value)
|
| +{
|
| + rtx fnaddr = XEXP (DECL_RTL (fndecl), 0);
|
| + rtx mem;
|
| +
|
| + emit_block_move (m_tramp, assemble_trampoline_template (),
|
| + GEN_INT (TRAMPOLINE_CODE_SIZE), BLOCK_OP_NORMAL);
|
| +
|
| + mem = adjust_address (m_tramp, Pmode, TRAMPOLINE_CODE_SIZE);
|
| + emit_move_insn (mem, fnaddr);
|
| + mem = adjust_address (m_tramp, Pmode,
|
| + TRAMPOLINE_CODE_SIZE + GET_MODE_SIZE (Pmode));
|
| + emit_move_insn (mem, chain_value);
|
| +}
|
| +
|
| #include "gt-iq2000.h"
|
|
|