1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093 |
- /* FR30 specific functions.
- Copyright (C) 1998-2015 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 "hash-set.h"
- #include "machmode.h"
- #include "vec.h"
- #include "double-int.h"
- #include "input.h"
- #include "alias.h"
- #include "symtab.h"
- #include "wide-int.h"
- #include "inchash.h"
- #include "tree.h"
- #include "fold-const.h"
- #include "stor-layout.h"
- #include "varasm.h"
- #include "output.h"
- #include "hashtab.h"
- #include "function.h"
- #include "statistics.h"
- #include "real.h"
- #include "fixed-value.h"
- #include "expmed.h"
- #include "dojump.h"
- #include "explow.h"
- #include "calls.h"
- #include "emit-rtl.h"
- #include "stmt.h"
- #include "expr.h"
- #include "obstack.h"
- #include "except.h"
- #include "dominance.h"
- #include "cfg.h"
- #include "cfgrtl.h"
- #include "cfganal.h"
- #include "lcm.h"
- #include "cfgbuild.h"
- #include "cfgcleanup.h"
- #include "predict.h"
- #include "basic-block.h"
- #include "df.h"
- #include "diagnostic-core.h"
- #include "tm_p.h"
- #include "target.h"
- #include "target-def.h"
- #include "builtins.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_t, machine_mode,
- tree, int *, int);
- static bool fr30_must_pass_in_stack (machine_mode, const_tree);
- static int fr30_arg_partial_bytes (cumulative_args_t, machine_mode,
- tree, bool);
- static rtx fr30_function_arg (cumulative_args_t, machine_mode,
- const_tree, bool);
- static void fr30_function_arg_advance (cumulative_args_t, 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 (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 (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
- /* 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
- 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_t arg_regs_used_so_far_v,
- machine_mode mode,
- tree type ATTRIBUTE_UNUSED,
- int *pretend_size,
- int second_time ATTRIBUTE_UNUSED)
- {
- CUMULATIVE_ARGS *arg_regs_used_so_far
- = get_cumulative_args (arg_regs_used_so_far_v);
- 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_v))
- /* If TARGET_STRICT_ARGUMENT_NAMING returns true, then the last named
- arg must not be treated as an anonymous arg. */
- /* ??? This is a pointer increment, which makes no sense. */
- 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 (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 (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 (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_t cum_v, machine_mode mode,
- tree type, bool named)
- {
- CUMULATIVE_ARGS *cum = get_cumulative_args (cum_v);
- /* 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_t cum_v, machine_mode mode,
- const_tree type, bool named)
- {
- CUMULATIVE_ARGS *cum = get_cumulative_args (cum_v);
- 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_t cum, machine_mode mode,
- const_tree type, bool named)
- {
- *get_cumulative_args (cum) += named * fr30_num_arg_regs (mode, type);
- }
- /*}}}*/
- /*{{{ Operand predicates */
- #ifndef Mmode
- #define Mmode 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);
- 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 (SImode, 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: */
|