diff options
Diffstat (limited to 'gcc/config/fr30/fr30.c')
-rw-r--r-- | gcc/config/fr30/fr30.c | 1066 |
1 files changed, 1066 insertions, 0 deletions
diff --git a/gcc/config/fr30/fr30.c b/gcc/config/fr30/fr30.c new file mode 100644 index 000000000..74585b5dc --- /dev/null +++ b/gcc/config/fr30/fr30.c @@ -0,0 +1,1066 @@ +/* FR30 specific functions. + Copyright (C) 1998, 1999, 2000, 2001, 2002, 2004, 2005, 2007, 2008, 2009, + 2010 Free Software Foundation, Inc. + Contributed by Cygnus Solutions. + + This file is part of GCC. + + GCC is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GCC is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GCC; see the file COPYING3. If not see + <http://www.gnu.org/licenses/>. */ + +/*{{{ Includes */ + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "tm.h" +#include "rtl.h" +#include "regs.h" +#include "hard-reg-set.h" +#include "insn-config.h" +#include "conditions.h" +#include "insn-attr.h" +#include "flags.h" +#include "recog.h" +#include "tree.h" +#include "output.h" +#include "expr.h" +#include "obstack.h" +#include "except.h" +#include "function.h" +#include "df.h" +#include "diagnostic-core.h" +#include "tm_p.h" +#include "target.h" +#include "target-def.h" + +/*}}}*/ +/*{{{ Function Prologues & Epilogues */ + +/* The FR30 stack looks like this: + + Before call After call + FP ->| | | | + +-----------------------+ +-----------------------+ high + | | | | memory + | local variables, | | local variables, | + | reg save area, etc. | | reg save area, etc. | + | | | | + +-----------------------+ +-----------------------+ + | | | | + | args to the func that | | args to this func. | + | is being called that | | | + SP ->| do not fit in regs | | | + +-----------------------+ +-----------------------+ + | args that used to be | \ + | in regs; only created | | pretend_size + AP-> | for vararg funcs | / + +-----------------------+ + | | \ + | register save area | | + | | | + +-----------------------+ | reg_size + | return address | | + +-----------------------+ | + FP ->| previous frame ptr | / + +-----------------------+ + | | \ + | local variables | | var_size + | | / + +-----------------------+ + | | \ + low | room for args to | | + memory | other funcs called | | args_size + | from this one | | + SP ->| | / + +-----------------------+ + + Note, AP is a fake hard register. It will be eliminated in favor of + SP or FP as appropriate. + + Note, Some or all of the stack sections above may be omitted if they + are not needed. */ + +/* Structure to be filled in by fr30_compute_frame_size() with register + save masks, and offsets for the current function. */ +struct fr30_frame_info +{ + unsigned int total_size; /* # Bytes that the entire frame takes up. */ + unsigned int pretend_size; /* # Bytes we push and pretend caller did. */ + unsigned int args_size; /* # Bytes that outgoing arguments take up. */ + unsigned int reg_size; /* # Bytes needed to store regs. */ + unsigned int var_size; /* # Bytes that variables take up. */ + unsigned int frame_size; /* # Bytes in current frame. */ + unsigned int gmask; /* Mask of saved registers. */ + unsigned int save_fp; /* Nonzero if frame pointer must be saved. */ + unsigned int save_rp; /* Nonzero if return pointer must be saved. */ + int initialised; /* Nonzero if frame size already calculated. */ +}; + +/* Current frame information calculated by fr30_compute_frame_size(). */ +static struct fr30_frame_info current_frame_info; + +/* Zero structure to initialize current_frame_info. */ +static struct fr30_frame_info zero_frame_info; + +static void fr30_setup_incoming_varargs (CUMULATIVE_ARGS *, enum machine_mode, + tree, int *, int); +static bool fr30_must_pass_in_stack (enum machine_mode, const_tree); +static int fr30_arg_partial_bytes (CUMULATIVE_ARGS *, enum machine_mode, + tree, bool); +static rtx fr30_function_arg (CUMULATIVE_ARGS *, enum machine_mode, + const_tree, bool); +static void fr30_function_arg_advance (CUMULATIVE_ARGS *, enum machine_mode, + const_tree, bool); +static bool fr30_frame_pointer_required (void); +static rtx fr30_function_value (const_tree, const_tree, bool); +static rtx fr30_libcall_value (enum machine_mode, const_rtx); +static bool fr30_function_value_regno_p (const unsigned int); +static bool fr30_can_eliminate (const int, const int); +static void fr30_asm_trampoline_template (FILE *); +static void fr30_trampoline_init (rtx, tree, rtx); +static int fr30_num_arg_regs (enum machine_mode, const_tree); + +#define FRAME_POINTER_MASK (1 << (FRAME_POINTER_REGNUM)) +#define RETURN_POINTER_MASK (1 << (RETURN_POINTER_REGNUM)) + +/* Tell prologue and epilogue if register REGNO should be saved / restored. + The return address and frame pointer are treated separately. + Don't consider them here. */ +#define MUST_SAVE_REGISTER(regno) \ + ( (regno) != RETURN_POINTER_REGNUM \ + && (regno) != FRAME_POINTER_REGNUM \ + && df_regs_ever_live_p (regno) \ + && ! call_used_regs [regno] ) + +#define MUST_SAVE_FRAME_POINTER (df_regs_ever_live_p (FRAME_POINTER_REGNUM) || frame_pointer_needed) +#define MUST_SAVE_RETURN_POINTER (df_regs_ever_live_p (RETURN_POINTER_REGNUM) || crtl->profile) + +#if UNITS_PER_WORD == 4 +#define WORD_ALIGN(SIZE) (((SIZE) + 3) & ~3) +#endif + +/* Implement TARGET_OPTION_OPTIMIZATION_TABLE. */ +static const struct default_options fr30_option_optimization_table[] = + { + { OPT_LEVELS_1_PLUS, OPT_fomit_frame_pointer, NULL, 1 }, + { OPT_LEVELS_NONE, 0, NULL, 0 } + }; + +/* Initialize the GCC target structure. */ +#undef TARGET_ASM_ALIGNED_HI_OP +#define TARGET_ASM_ALIGNED_HI_OP "\t.hword\t" +#undef TARGET_ASM_ALIGNED_SI_OP +#define TARGET_ASM_ALIGNED_SI_OP "\t.word\t" + +#undef TARGET_PROMOTE_PROTOTYPES +#define TARGET_PROMOTE_PROTOTYPES hook_bool_const_tree_true +#undef TARGET_PASS_BY_REFERENCE +#define TARGET_PASS_BY_REFERENCE hook_pass_by_reference_must_pass_in_stack +#undef TARGET_ARG_PARTIAL_BYTES +#define TARGET_ARG_PARTIAL_BYTES fr30_arg_partial_bytes +#undef TARGET_FUNCTION_ARG +#define TARGET_FUNCTION_ARG fr30_function_arg +#undef TARGET_FUNCTION_ARG_ADVANCE +#define TARGET_FUNCTION_ARG_ADVANCE fr30_function_arg_advance + +#undef TARGET_FUNCTION_VALUE +#define TARGET_FUNCTION_VALUE fr30_function_value +#undef TARGET_LIBCALL_VALUE +#define TARGET_LIBCALL_VALUE fr30_libcall_value +#undef TARGET_FUNCTION_VALUE_REGNO_P +#define TARGET_FUNCTION_VALUE_REGNO_P fr30_function_value_regno_p + +#undef TARGET_SETUP_INCOMING_VARARGS +#define TARGET_SETUP_INCOMING_VARARGS fr30_setup_incoming_varargs +#undef TARGET_MUST_PASS_IN_STACK +#define TARGET_MUST_PASS_IN_STACK fr30_must_pass_in_stack + +#undef TARGET_FRAME_POINTER_REQUIRED +#define TARGET_FRAME_POINTER_REQUIRED fr30_frame_pointer_required + +#undef TARGET_CAN_ELIMINATE +#define TARGET_CAN_ELIMINATE fr30_can_eliminate + +#undef TARGET_ASM_TRAMPOLINE_TEMPLATE +#define TARGET_ASM_TRAMPOLINE_TEMPLATE fr30_asm_trampoline_template +#undef TARGET_TRAMPOLINE_INIT +#define TARGET_TRAMPOLINE_INIT fr30_trampoline_init + +#undef TARGET_EXCEPT_UNWIND_INFO +#define TARGET_EXCEPT_UNWIND_INFO sjlj_except_unwind_info + +#undef TARGET_OPTION_OPTIMIZATION_TABLE +#define TARGET_OPTION_OPTIMIZATION_TABLE fr30_option_optimization_table + +struct gcc_target targetm = TARGET_INITIALIZER; + + +/* Worker function for TARGET_CAN_ELIMINATE. */ + +bool +fr30_can_eliminate (const int from ATTRIBUTE_UNUSED, const int to) +{ + return (to == FRAME_POINTER_REGNUM || ! frame_pointer_needed); +} + +/* Returns the number of bytes offset between FROM_REG and TO_REG + for the current function. As a side effect it fills in the + current_frame_info structure, if the data is available. */ +unsigned int +fr30_compute_frame_size (int from_reg, int to_reg) +{ + int regno; + unsigned int return_value; + unsigned int var_size; + unsigned int args_size; + unsigned int pretend_size; + unsigned int reg_size; + unsigned int gmask; + + var_size = WORD_ALIGN (get_frame_size ()); + args_size = WORD_ALIGN (crtl->outgoing_args_size); + pretend_size = crtl->args.pretend_args_size; + + reg_size = 0; + gmask = 0; + + /* Calculate space needed for registers. */ + for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno ++) + { + if (MUST_SAVE_REGISTER (regno)) + { + reg_size += UNITS_PER_WORD; + gmask |= 1 << regno; + } + } + + current_frame_info.save_fp = MUST_SAVE_FRAME_POINTER; + current_frame_info.save_rp = MUST_SAVE_RETURN_POINTER; + + reg_size += (current_frame_info.save_fp + current_frame_info.save_rp) + * UNITS_PER_WORD; + + /* Save computed information. */ + current_frame_info.pretend_size = pretend_size; + current_frame_info.var_size = var_size; + current_frame_info.args_size = args_size; + current_frame_info.reg_size = reg_size; + current_frame_info.frame_size = args_size + var_size; + current_frame_info.total_size = args_size + var_size + reg_size + pretend_size; + current_frame_info.gmask = gmask; + current_frame_info.initialised = reload_completed; + + /* Calculate the required distance. */ + return_value = 0; + + if (to_reg == STACK_POINTER_REGNUM) + return_value += args_size + var_size; + + if (from_reg == ARG_POINTER_REGNUM) + return_value += reg_size; + + return return_value; +} + +/* Called after register allocation to add any instructions needed for the + prologue. Using a prologue insn is favored compared to putting all of the + instructions in output_function_prologue(), since it allows the scheduler + to intermix instructions with the saves of the caller saved registers. In + some cases, it might be necessary to emit a barrier instruction as the last + insn to prevent such scheduling. */ + +void +fr30_expand_prologue (void) +{ + int regno; + rtx insn; + + if (! current_frame_info.initialised) + fr30_compute_frame_size (0, 0); + + /* This cases shouldn't happen. Catch it now. */ + gcc_assert (current_frame_info.total_size || !current_frame_info.gmask); + + /* Allocate space for register arguments if this is a variadic function. */ + if (current_frame_info.pretend_size) + { + int regs_to_save = current_frame_info.pretend_size / UNITS_PER_WORD; + + /* Push argument registers into the pretend arg area. */ + for (regno = FIRST_ARG_REGNUM + FR30_NUM_ARG_REGS; regno --, regs_to_save --;) + { + insn = emit_insn (gen_movsi_push (gen_rtx_REG (Pmode, regno))); + RTX_FRAME_RELATED_P (insn) = 1; + } + } + + if (current_frame_info.gmask) + { + /* Save any needed call-saved regs. */ + for (regno = STACK_POINTER_REGNUM; regno--;) + { + if ((current_frame_info.gmask & (1 << regno)) != 0) + { + insn = emit_insn (gen_movsi_push (gen_rtx_REG (Pmode, regno))); + RTX_FRAME_RELATED_P (insn) = 1; + } + } + } + + /* Save return address if necessary. */ + if (current_frame_info.save_rp) + { + insn = emit_insn (gen_movsi_push (gen_rtx_REG (Pmode, + RETURN_POINTER_REGNUM))); + RTX_FRAME_RELATED_P (insn) = 1; + } + + /* Save old frame pointer and create new one, if necessary. */ + if (current_frame_info.save_fp) + { + if (current_frame_info.frame_size < ((1 << 10) - UNITS_PER_WORD)) + { + int enter_size = current_frame_info.frame_size + UNITS_PER_WORD; + rtx pattern; + + insn = emit_insn (gen_enter_func (GEN_INT (enter_size))); + RTX_FRAME_RELATED_P (insn) = 1; + + pattern = PATTERN (insn); + + /* Also mark all 3 subexpressions as RTX_FRAME_RELATED_P. */ + if (GET_CODE (pattern) == PARALLEL) + { + int x; + for (x = XVECLEN (pattern, 0); x--;) + { + rtx part = XVECEXP (pattern, 0, x); + + /* One of the insns in the ENTER pattern updates the + frame pointer. If we do not actually need the frame + pointer in this function then this is a side effect + rather than a desired effect, so we do not mark that + insn as being related to the frame set up. Doing this + allows us to compile the crash66.C test file in the + G++ testsuite. */ + if (! frame_pointer_needed + && GET_CODE (part) == SET + && SET_DEST (part) == hard_frame_pointer_rtx) + RTX_FRAME_RELATED_P (part) = 0; + else + RTX_FRAME_RELATED_P (part) = 1; + } + } + } + else + { + insn = emit_insn (gen_movsi_push (frame_pointer_rtx)); + RTX_FRAME_RELATED_P (insn) = 1; + + if (frame_pointer_needed) + { + insn = emit_insn (gen_movsi (frame_pointer_rtx, stack_pointer_rtx)); + RTX_FRAME_RELATED_P (insn) = 1; + } + } + } + + /* Allocate the stack frame. */ + if (current_frame_info.frame_size == 0) + ; /* Nothing to do. */ + else if (current_frame_info.save_fp + && current_frame_info.frame_size < ((1 << 10) - UNITS_PER_WORD)) + ; /* Nothing to do. */ + else if (current_frame_info.frame_size <= 512) + { + insn = emit_insn (gen_add_to_stack + (GEN_INT (- (signed) current_frame_info.frame_size))); + RTX_FRAME_RELATED_P (insn) = 1; + } + else + { + rtx tmp = gen_rtx_REG (Pmode, PROLOGUE_TMP_REGNUM); + insn = emit_insn (gen_movsi (tmp, GEN_INT (current_frame_info.frame_size))); + RTX_FRAME_RELATED_P (insn) = 1; + insn = emit_insn (gen_subsi3 (stack_pointer_rtx, stack_pointer_rtx, tmp)); + RTX_FRAME_RELATED_P (insn) = 1; + } + + if (crtl->profile) + emit_insn (gen_blockage ()); +} + +/* Called after register allocation to add any instructions needed for the + epilogue. Using an epilogue insn is favored compared to putting all of the + instructions in output_function_epilogue(), since it allows the scheduler + to intermix instructions with the restores of the caller saved registers. + In some cases, it might be necessary to emit a barrier instruction as the + first insn to prevent such scheduling. */ +void +fr30_expand_epilogue (void) +{ + int regno; + + /* Perform the inversion operations of the prologue. */ + gcc_assert (current_frame_info.initialised); + + /* Pop local variables and arguments off the stack. + If frame_pointer_needed is TRUE then the frame pointer register + has actually been used as a frame pointer, and we can recover + the stack pointer from it, otherwise we must unwind the stack + manually. */ + if (current_frame_info.frame_size > 0) + { + if (current_frame_info.save_fp && frame_pointer_needed) + { + emit_insn (gen_leave_func ()); + current_frame_info.save_fp = 0; + } + else if (current_frame_info.frame_size <= 508) + emit_insn (gen_add_to_stack + (GEN_INT (current_frame_info.frame_size))); + else + { + rtx tmp = gen_rtx_REG (Pmode, PROLOGUE_TMP_REGNUM); + emit_insn (gen_movsi (tmp, GEN_INT (current_frame_info.frame_size))); + emit_insn (gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx, tmp)); + } + } + + if (current_frame_info.save_fp) + emit_insn (gen_movsi_pop (frame_pointer_rtx)); + + /* Pop all the registers that were pushed. */ + if (current_frame_info.save_rp) + emit_insn (gen_movsi_pop (gen_rtx_REG (Pmode, RETURN_POINTER_REGNUM))); + + for (regno = 0; regno < STACK_POINTER_REGNUM; regno ++) + if (current_frame_info.gmask & (1 << regno)) + emit_insn (gen_movsi_pop (gen_rtx_REG (Pmode, regno))); + + if (current_frame_info.pretend_size) + emit_insn (gen_add_to_stack (GEN_INT (current_frame_info.pretend_size))); + + /* Reset state info for each function. */ + current_frame_info = zero_frame_info; + + emit_jump_insn (gen_return_from_func ()); +} + +/* Do any needed setup for a variadic function. We must create a register + parameter block, and then copy any anonymous arguments, plus the last + named argument, from registers into memory. * copying actually done in + fr30_expand_prologue(). + + ARG_REGS_USED_SO_FAR has *not* been updated for the last named argument + which has type TYPE and mode MODE, and we rely on this fact. */ +void +fr30_setup_incoming_varargs (CUMULATIVE_ARGS *arg_regs_used_so_far, + enum machine_mode mode, + tree type ATTRIBUTE_UNUSED, + int *pretend_size, + int second_time ATTRIBUTE_UNUSED) +{ + int size; + + /* All BLKmode values are passed by reference. */ + gcc_assert (mode != BLKmode); + + /* ??? This run-time test as well as the code inside the if + statement is probably unnecessary. */ + if (targetm.calls.strict_argument_naming (arg_regs_used_so_far)) + /* If TARGET_STRICT_ARGUMENT_NAMING returns true, then the last named + arg must not be treated as an anonymous arg. */ + arg_regs_used_so_far += fr30_num_arg_regs (mode, type); + + size = FR30_NUM_ARG_REGS - (* arg_regs_used_so_far); + + if (size <= 0) + return; + + * pretend_size = (size * UNITS_PER_WORD); +} + +/*}}}*/ +/*{{{ Printing operands */ + +/* Print a memory address as an operand to reference that memory location. */ + +void +fr30_print_operand_address (FILE *stream, rtx address) +{ + switch (GET_CODE (address)) + { + case SYMBOL_REF: + output_addr_const (stream, address); + break; + + default: + fprintf (stderr, "code = %x\n", GET_CODE (address)); + debug_rtx (address); + output_operand_lossage ("fr30_print_operand_address: unhandled address"); + break; + } +} + +/* Print an operand. */ + +void +fr30_print_operand (FILE *file, rtx x, int code) +{ + rtx x0; + + switch (code) + { + case '#': + /* Output a :D if this instruction is delayed. */ + if (dbr_sequence_length () != 0) + fputs (":D", file); + return; + + case 'p': + /* Compute the register name of the second register in a hi/lo + register pair. */ + if (GET_CODE (x) != REG) + output_operand_lossage ("fr30_print_operand: unrecognized %%p code"); + else + fprintf (file, "r%d", REGNO (x) + 1); + return; + + case 'b': + /* Convert GCC's comparison operators into FR30 comparison codes. */ + switch (GET_CODE (x)) + { + case EQ: fprintf (file, "eq"); break; + case NE: fprintf (file, "ne"); break; + case LT: fprintf (file, "lt"); break; + case LE: fprintf (file, "le"); break; + case GT: fprintf (file, "gt"); break; + case GE: fprintf (file, "ge"); break; + case LTU: fprintf (file, "c"); break; + case LEU: fprintf (file, "ls"); break; + case GTU: fprintf (file, "hi"); break; + case GEU: fprintf (file, "nc"); break; + default: + output_operand_lossage ("fr30_print_operand: unrecognized %%b code"); + break; + } + return; + + case 'B': + /* Convert GCC's comparison operators into the complimentary FR30 + comparison codes. */ + switch (GET_CODE (x)) + { + case EQ: fprintf (file, "ne"); break; + case NE: fprintf (file, "eq"); break; + case LT: fprintf (file, "ge"); break; + case LE: fprintf (file, "gt"); break; + case GT: fprintf (file, "le"); break; + case GE: fprintf (file, "lt"); break; + case LTU: fprintf (file, "nc"); break; + case LEU: fprintf (file, "hi"); break; + case GTU: fprintf (file, "ls"); break; + case GEU: fprintf (file, "c"); break; + default: + output_operand_lossage ("fr30_print_operand: unrecognized %%B code"); + break; + } + return; + + case 'A': + /* Print a signed byte value as an unsigned value. */ + if (GET_CODE (x) != CONST_INT) + output_operand_lossage ("fr30_print_operand: invalid operand to %%A code"); + else + { + HOST_WIDE_INT val; + + val = INTVAL (x); + + val &= 0xff; + + fprintf (file, HOST_WIDE_INT_PRINT_DEC, val); + } + return; + + case 'x': + if (GET_CODE (x) != CONST_INT + || INTVAL (x) < 16 + || INTVAL (x) > 32) + output_operand_lossage ("fr30_print_operand: invalid %%x code"); + else + fprintf (file, HOST_WIDE_INT_PRINT_DEC, INTVAL (x) - 16); + return; + + case 'F': + if (GET_CODE (x) != CONST_DOUBLE) + output_operand_lossage ("fr30_print_operand: invalid %%F code"); + else + { + char str[30]; + + real_to_decimal (str, CONST_DOUBLE_REAL_VALUE (x), + sizeof (str), 0, 1); + fputs (str, file); + } + return; + + case 0: + /* Handled below. */ + break; + + default: + fprintf (stderr, "unknown code = %x\n", code); + output_operand_lossage ("fr30_print_operand: unknown code"); + return; + } + + switch (GET_CODE (x)) + { + case REG: + fputs (reg_names [REGNO (x)], file); + break; + + case MEM: + x0 = XEXP (x,0); + + switch (GET_CODE (x0)) + { + case REG: + gcc_assert ((unsigned) REGNO (x0) < ARRAY_SIZE (reg_names)); + fprintf (file, "@%s", reg_names [REGNO (x0)]); + break; + + case PLUS: + if (GET_CODE (XEXP (x0, 0)) != REG + || REGNO (XEXP (x0, 0)) < FRAME_POINTER_REGNUM + || REGNO (XEXP (x0, 0)) > STACK_POINTER_REGNUM + || GET_CODE (XEXP (x0, 1)) != CONST_INT) + { + fprintf (stderr, "bad INDEXed address:"); + debug_rtx (x); + output_operand_lossage ("fr30_print_operand: unhandled MEM"); + } + else if (REGNO (XEXP (x0, 0)) == FRAME_POINTER_REGNUM) + { + HOST_WIDE_INT val = INTVAL (XEXP (x0, 1)); + if (val < -(1 << 9) || val > ((1 << 9) - 4)) + { + fprintf (stderr, "frame INDEX out of range:"); + debug_rtx (x); + output_operand_lossage ("fr30_print_operand: unhandled MEM"); + } + fprintf (file, "@(r14, #" HOST_WIDE_INT_PRINT_DEC ")", val); + } + else + { + HOST_WIDE_INT val = INTVAL (XEXP (x0, 1)); + if (val < 0 || val > ((1 << 6) - 4)) + { + fprintf (stderr, "stack INDEX out of range:"); + debug_rtx (x); + output_operand_lossage ("fr30_print_operand: unhandled MEM"); + } + fprintf (file, "@(r15, #" HOST_WIDE_INT_PRINT_DEC ")", val); + } + break; + + case SYMBOL_REF: + output_address (x0); + break; + + default: + fprintf (stderr, "bad MEM code = %x\n", GET_CODE (x0)); + debug_rtx (x); + output_operand_lossage ("fr30_print_operand: unhandled MEM"); + break; + } + break; + + case CONST_DOUBLE : + /* We handle SFmode constants here as output_addr_const doesn't. */ + if (GET_MODE (x) == SFmode) + { + REAL_VALUE_TYPE d; + long l; + + REAL_VALUE_FROM_CONST_DOUBLE (d, x); + REAL_VALUE_TO_TARGET_SINGLE (d, l); + fprintf (file, "0x%08lx", l); + break; + } + + /* Fall through. Let output_addr_const deal with it. */ + default: + output_addr_const (file, x); + break; + } + + return; +} + +/*}}}*/ + +/* Implements TARGET_FUNCTION_VALUE. */ + +static rtx +fr30_function_value (const_tree valtype, + const_tree fntype_or_decli ATTRIBUTE_UNUSED, + bool outgoing ATTRIBUTE_UNUSED) +{ + return gen_rtx_REG (TYPE_MODE (valtype), RETURN_VALUE_REGNUM); +} + +/* Implements TARGET_LIBCALL_VALUE. */ + +static rtx +fr30_libcall_value (enum machine_mode mode, + const_rtx fun ATTRIBUTE_UNUSED) +{ + return gen_rtx_REG (mode, RETURN_VALUE_REGNUM); +} + +/* Implements TARGET_FUNCTION_VALUE_REGNO_P. */ + +static bool +fr30_function_value_regno_p (const unsigned int regno) +{ + return (regno == RETURN_VALUE_REGNUM); +} + +/*{{{ Function arguments */ + +/* Return true if we should pass an argument on the stack rather than + in registers. */ + +static bool +fr30_must_pass_in_stack (enum machine_mode mode, const_tree type) +{ + if (mode == BLKmode) + return true; + if (type == NULL) + return false; + return AGGREGATE_TYPE_P (type); +} + +/* Compute the number of word sized registers needed to hold a + function argument of mode INT_MODE and tree type TYPE. */ +static int +fr30_num_arg_regs (enum machine_mode mode, const_tree type) +{ + int size; + + if (targetm.calls.must_pass_in_stack (mode, type)) + return 0; + + if (type && mode == BLKmode) + size = int_size_in_bytes (type); + else + size = GET_MODE_SIZE (mode); + + return (size + UNITS_PER_WORD - 1) / UNITS_PER_WORD; +} + +/* Returns the number of bytes in which *part* of a parameter of machine + mode MODE and tree type TYPE (which may be NULL if the type is not known). + If the argument fits entirely in the argument registers, or entirely on + the stack, then 0 is returned. + CUM is the number of argument registers already used by earlier + parameters to the function. */ + +static int +fr30_arg_partial_bytes (CUMULATIVE_ARGS *cum, enum machine_mode mode, + tree type, bool named) +{ + /* Unnamed arguments, i.e. those that are prototyped as ... + are always passed on the stack. + Also check here to see if all the argument registers are full. */ + if (named == 0 || *cum >= FR30_NUM_ARG_REGS) + return 0; + + /* Work out how many argument registers would be needed if this + parameter were to be passed entirely in registers. If there + are sufficient argument registers available (or if no registers + are needed because the parameter must be passed on the stack) + then return zero, as this parameter does not require partial + register, partial stack stack space. */ + if (*cum + fr30_num_arg_regs (mode, type) <= FR30_NUM_ARG_REGS) + return 0; + + return (FR30_NUM_ARG_REGS - *cum) * UNITS_PER_WORD; +} + +static rtx +fr30_function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode, + const_tree type, bool named) +{ + if (!named + || fr30_must_pass_in_stack (mode, type) + || *cum >= FR30_NUM_ARG_REGS) + return NULL_RTX; + else + return gen_rtx_REG (mode, *cum + FIRST_ARG_REGNUM); +} + +/* A C statement (sans semicolon) to update the summarizer variable CUM to + advance past an argument in the argument list. The values MODE, TYPE and + NAMED describe that argument. Once this is done, the variable CUM is + suitable for analyzing the *following* argument with `FUNCTION_ARG', etc. + + This macro need not do anything if the argument in question was passed on + the stack. The compiler knows how to track the amount of stack space used + for arguments without any special help. */ +static void +fr30_function_arg_advance (CUMULATIVE_ARGS *cum, enum machine_mode mode, + const_tree type, bool named) +{ + *cum += named * fr30_num_arg_regs (mode, type); +} + +/*}}}*/ +/*{{{ Operand predicates */ + +#ifndef Mmode +#define Mmode enum machine_mode +#endif + +/* Returns true iff all the registers in the operands array + are in descending or ascending order. */ +int +fr30_check_multiple_regs (rtx *operands, int num_operands, int descending) +{ + if (descending) + { + unsigned int prev_regno = 0; + + while (num_operands --) + { + if (GET_CODE (operands [num_operands]) != REG) + return 0; + + if (REGNO (operands [num_operands]) < prev_regno) + return 0; + + prev_regno = REGNO (operands [num_operands]); + } + } + else + { + unsigned int prev_regno = CONDITION_CODE_REGNUM; + + while (num_operands --) + { + if (GET_CODE (operands [num_operands]) != REG) + return 0; + + if (REGNO (operands [num_operands]) > prev_regno) + return 0; + + prev_regno = REGNO (operands [num_operands]); + } + } + + return 1; +} + +int +fr30_const_double_is_zero (rtx operand) +{ + REAL_VALUE_TYPE d; + + if (operand == NULL || GET_CODE (operand) != CONST_DOUBLE) + return 0; + + REAL_VALUE_FROM_CONST_DOUBLE (d, operand); + + return REAL_VALUES_EQUAL (d, dconst0); +} + +/*}}}*/ +/*{{{ Instruction Output Routines */ + +/* Output a double word move. + It must be REG<-REG, REG<-MEM, MEM<-REG or REG<-CONST. + On the FR30 we are constrained by the fact that it does not + support offsetable addresses, and so we have to load the + address of the secnd word into the second destination register + before we can use it. */ + +rtx +fr30_move_double (rtx * operands) +{ + rtx src = operands[1]; + rtx dest = operands[0]; + enum rtx_code src_code = GET_CODE (src); + enum rtx_code dest_code = GET_CODE (dest); + enum machine_mode mode = GET_MODE (dest); + rtx val; + + start_sequence (); + + if (dest_code == REG) + { + if (src_code == REG) + { + int reverse = (REGNO (dest) == REGNO (src) + 1); + + /* We normally copy the low-numbered register first. However, if + the first register of operand 0 is the same as the second register + of operand 1, we must copy in the opposite order. */ + emit_insn (gen_rtx_SET (VOIDmode, + operand_subword (dest, reverse, TRUE, mode), + operand_subword (src, reverse, TRUE, mode))); + + emit_insn (gen_rtx_SET (VOIDmode, + operand_subword (dest, !reverse, TRUE, mode), + operand_subword (src, !reverse, TRUE, mode))); + } + else if (src_code == MEM) + { + rtx addr = XEXP (src, 0); + rtx dest0 = operand_subword (dest, 0, TRUE, mode); + rtx dest1 = operand_subword (dest, 1, TRUE, mode); + rtx new_mem; + + gcc_assert (GET_CODE (addr) == REG); + + /* Copy the address before clobbering it. See PR 34174. */ + emit_insn (gen_rtx_SET (SImode, dest1, addr)); + emit_insn (gen_rtx_SET (VOIDmode, dest0, + adjust_address (src, SImode, 0))); + emit_insn (gen_rtx_SET (SImode, dest1, + plus_constant (dest1, UNITS_PER_WORD))); + + new_mem = gen_rtx_MEM (SImode, dest1); + MEM_COPY_ATTRIBUTES (new_mem, src); + + emit_insn (gen_rtx_SET (VOIDmode, dest1, new_mem)); + } + else if (src_code == CONST_INT || src_code == CONST_DOUBLE) + { + rtx words[2]; + split_double (src, &words[0], &words[1]); + emit_insn (gen_rtx_SET (VOIDmode, + operand_subword (dest, 0, TRUE, mode), + words[0])); + + emit_insn (gen_rtx_SET (VOIDmode, + operand_subword (dest, 1, TRUE, mode), + words[1])); + } + } + else if (src_code == REG && dest_code == MEM) + { + rtx addr = XEXP (dest, 0); + rtx src0; + rtx src1; + + gcc_assert (GET_CODE (addr) == REG); + + src0 = operand_subword (src, 0, TRUE, mode); + src1 = operand_subword (src, 1, TRUE, mode); + + emit_move_insn (adjust_address (dest, SImode, 0), src0); + + if (REGNO (addr) == STACK_POINTER_REGNUM + || REGNO (addr) == FRAME_POINTER_REGNUM) + emit_insn (gen_rtx_SET (VOIDmode, + adjust_address (dest, SImode, UNITS_PER_WORD), + src1)); + else + { + rtx new_mem; + rtx scratch_reg_r0 = gen_rtx_REG (SImode, 0); + + /* We need a scratch register to hold the value of 'address + 4'. + We use r0 for this purpose. It is used for example for long + jumps and is already marked to not be used by normal register + allocation. */ + emit_insn (gen_movsi_internal (scratch_reg_r0, addr)); + emit_insn (gen_addsi_small_int (scratch_reg_r0, scratch_reg_r0, + GEN_INT (UNITS_PER_WORD))); + new_mem = gen_rtx_MEM (SImode, scratch_reg_r0); + MEM_COPY_ATTRIBUTES (new_mem, dest); + emit_move_insn (new_mem, src1); + emit_insn (gen_blockage ()); + } + } + else + /* This should have been prevented by the constraints on movdi_insn. */ + gcc_unreachable (); + + val = get_insns (); + end_sequence (); + + return val; +} + +/* Implement TARGET_FRAME_POINTER_REQUIRED. */ + +bool +fr30_frame_pointer_required (void) +{ + return (flag_omit_frame_pointer == 0 || crtl->args.pretend_args_size > 0); +} + +/*}}}*/ +/*{{{ Trampoline Output Routines */ + +/* Implement TARGET_ASM_TRAMPOLINE_TEMPLATE. + On the FR30, the trampoline is: + + nop + ldi:32 STATIC, r12 + nop + ldi:32 FUNCTION, r0 + jmp @r0 + + The no-ops are to guarantee that the static chain and final + target are 32 bit aligned within the trampoline. That allows us to + initialize those locations with simple SImode stores. The alternative + would be to use HImode stores. */ + +static void +fr30_asm_trampoline_template (FILE *f) +{ + fprintf (f, "\tnop\n"); + fprintf (f, "\tldi:32\t#0, %s\n", reg_names [STATIC_CHAIN_REGNUM]); + fprintf (f, "\tnop\n"); + fprintf (f, "\tldi:32\t#0, %s\n", reg_names [COMPILER_SCRATCH_REGISTER]); + fprintf (f, "\tjmp\t@%s\n", reg_names [COMPILER_SCRATCH_REGISTER]); +} + +/* Implement TARGET_TRAMPOLINE_INIT. */ + +static void +fr30_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_SIZE), BLOCK_OP_NORMAL); + + mem = adjust_address (m_tramp, SImode, 4); + emit_move_insn (mem, chain_value); + mem = adjust_address (m_tramp, SImode, 12); + emit_move_insn (mem, fnaddr); +} + +/*}}}*/ +/* Local Variables: */ +/* folded-file: t */ +/* End: */ |