+2005-04-05 Bernd Schmidt <bernd.schmidt@analog.com>
+
+ * config/bfin/bfin-modes.def: New file.
+ * config/bfin/bfin-protos.h: New file.
+ * config/bfin/bfin.c: New file.
+ * config/bfin/bfin.h: New file.
+ * config/bfin/bfin.md: New file.
+ * config/bfin/bfin.opt: New file.
+ * config/bfin/crti.s: New file.
+ * config/bfin/crtn.s: New file.
+ * config/bfin/elf.h: New file.
+ * config/bfin/lib1funcs.asm: New file.
+ * config/bfin/predicates.md: New file.
+ * config/bfin/t-bfin: New file.
+ * config/bfin/t-bfin-elf: New file.
+ * doc/extend.texi (exception_handler, kspisusp, nesting, nmi_handler):
+ Document new attributes.
+ (interrupt, interrupt_handler, saveall): Update documentation for
+ these attributes.
+ * doc/install.texi (Specific): Add entry for the Blackfin.
+ * doc/invoke.texi (Blackfin Options): New section.
+ * doc/md.texi (Blackfin family): New section to document constraints.
+ * config.gcc: Add bfin*-* and bfin*-elf configurations.
+
2005-04-05 Olivier Hainque <hainque@adacore.com>
* config/mips/iris6.h (DWARF_FRAME_RETURN_COLUMN): Redefine to
cpu_type=arm
extra_headers="mmintrin.h"
;;
+bfin*-*)
+ cpu_type=bfin
+ ;;
ep9312*-*-*)
cpu_type=arm
;;
tm_file="avr/avr.h dbxelf.h"
use_fixproto=yes
;;
+bfin*-elf*)
+ tm_file="${tm_file} dbxelf.h elfos.h bfin/elf.h"
+ tmake_file=bfin/t-bfin-elf
+ use_collect2=no
+ ;;
+bfin*-*)
+ tm_file="${tm_file} dbxelf.h elfos.h bfin/elf.h"
+ tmake_file=bfin/t-bfin
+ use_collect2=no
+ ;;
c4x-*-rtems* | tic4x-*-rtems*)
tmake_file="c4x/t-c4x t-rtems c4x/t-rtems"
tm_file="c4x/c4x.h c4x/rtems.h rtems.h"
--- /dev/null
+/* Definitions of target machine for GNU compiler, for Blackfin.
+ Copyright (C) 2005 Free Software Foundation, Inc.
+ Contributed by Analog Devices.
+
+ 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 2, 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 COPYING. If not, write to the
+ Free Software Foundation, 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA. */
+
+/* PDImode for the 40 bit accumulators. */
+PARTIAL_INT_MODE (DI);
+
+VECTOR_MODE (INT, HI, 2); /* V2HI */
--- /dev/null
+/* Prototypes for Blackfin functions used in the md file & elsewhere.
+ Copyright (C) 2005 Free Software Foundation, Inc.
+
+ This file is part of GNU CC.
+
+ GNU CC 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 2, or (at your option)
+ any later version.
+
+ GNU CC 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 GNU CC; see the file COPYING. If not, write to
+ the Free Software Foundation, 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA. */
+
+/* Function prototypes that cannot exist in bfin.h due to dependency
+ complications. */
+#ifndef GCC_BFIN_PROTOS_H
+#define GCC_BFIN_PROTOS_H
+
+#define Mmode enum machine_mode
+
+extern rtx function_arg (CUMULATIVE_ARGS *, Mmode, tree, int);
+extern void function_arg_advance (CUMULATIVE_ARGS *, Mmode, tree, int);
+extern bool function_arg_regno_p (int);
+
+extern const char *output_load_immediate (rtx *);
+extern const char *output_casesi_internal (rtx *);
+extern char *bfin_asm_long (void);
+extern char *bfin_asm_short (void);
+extern int log2constp (unsigned HOST_WIDE_INT);
+
+extern rtx legitimize_address (rtx, rtx, Mmode);
+extern int hard_regno_mode_ok (int, Mmode);
+extern void init_cumulative_args (CUMULATIVE_ARGS *, tree, rtx);
+extern int bfin_frame_pointer_required (void);
+extern HOST_WIDE_INT bfin_initial_elimination_offset (int, int);
+
+extern int effective_address_32bit_p (rtx, Mmode);
+extern int symbolic_reference_mentioned_p (rtx);
+extern rtx bfin_gen_compare (rtx, Mmode);
+extern void expand_move (rtx *, Mmode);
+extern void bfin_expand_call (rtx, rtx, rtx, int);
+extern bool bfin_expand_strmov (rtx, rtx, rtx, rtx);
+
+extern void conditional_register_usage (void);
+extern int bfin_register_move_cost (enum machine_mode, enum reg_class,
+ enum reg_class);
+extern int bfin_memory_move_cost (enum machine_mode, enum reg_class, int in);
+extern enum reg_class secondary_input_reload_class (enum reg_class, Mmode,
+ rtx);
+extern enum reg_class secondary_output_reload_class (enum reg_class, Mmode,
+ rtx);
+extern char *section_asm_op_1 (SECT_ENUM_T);
+extern char *section_asm_op (SECT_ENUM_T);
+extern void override_options (void);
+extern void print_operand (FILE *, rtx, char);
+extern void print_address_operand (FILE *, rtx);
+extern void split_di (rtx [], int, rtx [], rtx []);
+extern int split_load_immediate (rtx []);
+extern rtx legitimize_pic_address (rtx, rtx);
+extern void emit_pic_move (rtx *, Mmode);
+extern void override_options (void);
+extern void asm_conditional_branch (rtx, rtx *, int, int);
+extern rtx bfin_gen_compare (rtx, Mmode);
+
+extern int bfin_return_in_memory (tree);
+extern void initialize_trampoline (rtx, rtx, rtx);
+extern bool bfin_legitimate_address_p (Mmode, rtx, int);
+extern rtx bfin_va_arg (tree, tree);
+
+extern void bfin_expand_prologue (void);
+extern void bfin_expand_epilogue (int, int);
+extern int push_multiple_operation (rtx, Mmode);
+extern int pop_multiple_operation (rtx, Mmode);
+extern void output_push_multiple (rtx, rtx *);
+extern void output_pop_multiple (rtx, rtx *);
+extern int bfin_hard_regno_rename_ok (unsigned int, unsigned int);
+extern rtx bfin_return_addr_rtx (int);
+#undef Mmode
+
+#endif
+
--- /dev/null
+/* The Blackfin code generation auxilary output file.
+ Copyright (C) 2005 Free Software Foundation, Inc.
+ Contributed by Analog Devices.
+
+ 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 2, 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 COPYING. If not, write to
+ the Free Software Foundation, 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA. */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tm.h"
+#include "rtl.h"
+#include "regs.h"
+#include "hard-reg-set.h"
+#include "real.h"
+#include "insn-config.h"
+#include "conditions.h"
+#include "insn-flags.h"
+#include "output.h"
+#include "insn-attr.h"
+#include "tree.h"
+#include "flags.h"
+#include "except.h"
+#include "function.h"
+#include "input.h"
+#include "target.h"
+#include "target-def.h"
+#include "expr.h"
+#include "toplev.h"
+#include "recog.h"
+#include "ggc.h"
+#include "integrate.h"
+#include "bfin-protos.h"
+#include "tm-preds.h"
+#include "gt-bfin.h"
+
+/* Test and compare insns in bfin.md store the information needed to
+ generate branch and scc insns here. */
+rtx bfin_compare_op0, bfin_compare_op1;
+
+/* RTX for condition code flag register and RETS register */
+extern GTY(()) rtx bfin_cc_rtx;
+extern GTY(()) rtx bfin_rets_rtx;
+rtx bfin_cc_rtx, bfin_rets_rtx;
+
+int max_arg_registers = 0;
+
+/* Arrays used when emitting register names. */
+const char *short_reg_names[] = SHORT_REGISTER_NAMES;
+const char *high_reg_names[] = HIGH_REGISTER_NAMES;
+const char *dregs_pair_names[] = DREGS_PAIR_NAMES;
+const char *byte_reg_names[] = BYTE_REGISTER_NAMES;
+
+static int arg_regs[] = FUNCTION_ARG_REGISTERS;
+
+const char *bfin_library_id_string;
+
+static void
+bfin_globalize_label (FILE *stream, const char *name)
+{
+ fputs (".global ", stream);
+ assemble_name (stream, name);
+ fputc (';',stream);
+ fputc ('\n',stream);
+}
+
+static void
+output_file_start (void)
+{
+ FILE *file = asm_out_file;
+ int i;
+
+ fprintf (file, ".file \"%s\";\n", input_filename);
+
+ for (i = 0; arg_regs[i] >= 0; i++)
+ ;
+ max_arg_registers = i; /* how many arg reg used */
+}
+
+/* Called early in the compilation to conditionally modify
+ fixed_regs/call_used_regs. */
+
+void
+conditional_register_usage (void)
+{
+ /* initialize condition code flag register rtx */
+ bfin_cc_rtx = gen_rtx_REG (BImode, REG_CC);
+ bfin_rets_rtx = gen_rtx_REG (Pmode, REG_RETS);
+}
+
+/* Examine machine-dependent attributes of function type FUNTYPE and return its
+ type. See the definition of E_FUNKIND. */
+
+static e_funkind funkind (tree funtype)
+{
+ tree attrs = TYPE_ATTRIBUTES (funtype);
+ if (lookup_attribute ("interrupt_handler", attrs))
+ return INTERRUPT_HANDLER;
+ else if (lookup_attribute ("exception_handler", attrs))
+ return EXCPT_HANDLER;
+ else if (lookup_attribute ("nmi_handler", attrs))
+ return NMI_HANDLER;
+ else
+ return SUBROUTINE;
+}
+\f
+/* Stack frame layout. */
+
+/* Compute the number of DREGS to save with a push_multiple operation.
+ This could include registers that aren't modified in the function,
+ since push_multiple only takes a range of registers. */
+
+static int
+n_dregs_to_save (void)
+{
+ unsigned i;
+
+ for (i = REG_R0; i <= REG_R7; i++)
+ {
+ if (regs_ever_live[i] && ! call_used_regs[i])
+ return REG_R7 - i + 1;
+
+ if (current_function_calls_eh_return)
+ {
+ unsigned j;
+ for (j = 0; ; j++)
+ {
+ unsigned test = EH_RETURN_DATA_REGNO (j);
+ if (test == INVALID_REGNUM)
+ break;
+ if (test == i)
+ return REG_R7 - i + 1;
+ }
+ }
+
+ }
+ return 0;
+}
+
+/* Like n_dregs_to_save, but compute number of PREGS to save. */
+
+static int
+n_pregs_to_save (void)
+{
+ unsigned i;
+
+ for (i = REG_P0; i <= REG_P5; i++)
+ if ((regs_ever_live[i] && ! call_used_regs[i])
+ || (i == PIC_OFFSET_TABLE_REGNUM
+ && (current_function_uses_pic_offset_table
+ || (TARGET_ID_SHARED_LIBRARY && ! current_function_is_leaf))))
+ return REG_P5 - i + 1;
+ return 0;
+}
+
+/* Determine if we are going to save the frame pointer in the prologue. */
+
+static bool
+must_save_fp_p (void)
+{
+ return (frame_pointer_needed || regs_ever_live[REG_FP]);
+}
+
+static bool
+stack_frame_needed_p (void)
+{
+ /* EH return puts a new return address into the frame using an
+ address relative to the frame pointer. */
+ if (current_function_calls_eh_return)
+ return true;
+ return frame_pointer_needed;
+}
+
+/* Emit code to save registers in the prologue. SAVEALL is nonzero if we
+ must save all registers; this is used for interrupt handlers.
+ SPREG contains (reg:SI REG_SP). */
+
+static void
+expand_prologue_reg_save (rtx spreg, int saveall)
+{
+ int ndregs = saveall ? 8 : n_dregs_to_save ();
+ int npregs = saveall ? 6 : n_pregs_to_save ();
+ int dregno = REG_R7 + 1 - ndregs;
+ int pregno = REG_P5 + 1 - npregs;
+ int total = ndregs + npregs;
+ int i;
+ rtx pat, insn, val;
+
+ if (total == 0)
+ return;
+
+ val = GEN_INT (-total * 4);
+ pat = gen_rtx_PARALLEL (VOIDmode, rtvec_alloc (total + 2));
+ XVECEXP (pat, 0, 0) = gen_rtx_UNSPEC (VOIDmode, gen_rtvec (1, val),
+ UNSPEC_PUSH_MULTIPLE);
+ XVECEXP (pat, 0, total + 1) = gen_rtx_SET (VOIDmode, spreg,
+ gen_rtx_PLUS (Pmode, spreg,
+ val));
+ RTX_FRAME_RELATED_P (XVECEXP (pat, 0, total + 1)) = 1;
+ for (i = 0; i < total; i++)
+ {
+ rtx memref = gen_rtx_MEM (word_mode,
+ gen_rtx_PLUS (Pmode, spreg,
+ GEN_INT (- i * 4 - 4)));
+ rtx subpat;
+ if (ndregs > 0)
+ {
+ subpat = gen_rtx_SET (VOIDmode, memref, gen_rtx_REG (word_mode,
+ dregno++));
+ ndregs--;
+ }
+ else
+ {
+ subpat = gen_rtx_SET (VOIDmode, memref, gen_rtx_REG (word_mode,
+ pregno++));
+ npregs++;
+ }
+ XVECEXP (pat, 0, i + 1) = subpat;
+ RTX_FRAME_RELATED_P (subpat) = 1;
+ }
+ insn = emit_insn (pat);
+ RTX_FRAME_RELATED_P (insn) = 1;
+}
+
+/* Emit code to restore registers in the epilogue. SAVEALL is nonzero if we
+ must save all registers; this is used for interrupt handlers.
+ SPREG contains (reg:SI REG_SP). */
+
+static void
+expand_epilogue_reg_restore (rtx spreg, int saveall)
+{
+ int ndregs = saveall ? 8 : n_dregs_to_save ();
+ int npregs = saveall ? 6 : n_pregs_to_save ();
+ int total = ndregs + npregs;
+ int i, regno;
+ rtx pat, insn;
+
+ if (total == 0)
+ return;
+
+ pat = gen_rtx_PARALLEL (VOIDmode, rtvec_alloc (total + 1));
+ XVECEXP (pat, 0, 0) = gen_rtx_SET (VOIDmode, spreg,
+ gen_rtx_PLUS (Pmode, spreg,
+ GEN_INT (total * 4)));
+
+ if (npregs > 0)
+ regno = REG_P5 + 1;
+ else
+ regno = REG_R7 + 1;
+
+ for (i = 0; i < total; i++)
+ {
+ rtx addr = (i > 0
+ ? gen_rtx_PLUS (Pmode, spreg, GEN_INT (i * 4))
+ : spreg);
+ rtx memref = gen_rtx_MEM (word_mode, addr);
+
+ regno--;
+ XVECEXP (pat, 0, i + 1)
+ = gen_rtx_SET (VOIDmode, gen_rtx_REG (word_mode, regno), memref);
+
+ if (npregs > 0)
+ {
+ if (--npregs == 0)
+ regno = REG_R7 + 1;
+ }
+ }
+
+ insn = emit_insn (pat);
+ RTX_FRAME_RELATED_P (insn) = 1;
+}
+
+/* Perform any needed actions needed for a function that is receiving a
+ variable number of arguments.
+
+ CUM is as above.
+
+ MODE and TYPE are the mode and type of the current parameter.
+
+ PRETEND_SIZE is a variable that should be set to the amount of stack
+ that must be pushed by the prolog to pretend that our caller pushed
+ it.
+
+ Normally, this macro will push all remaining incoming registers on the
+ stack and set PRETEND_SIZE to the length of the registers pushed.
+
+ Blackfin specific :
+ - VDSP C compiler manual (our ABI) says that a variable args function
+ should save the R0, R1 and R2 registers in the stack.
+ - The caller will always leave space on the stack for the
+ arguments that are passed in registers, so we dont have
+ to leave any extra space.
+ - now, the vastart pointer can access all arguments from the stack. */
+
+static void
+setup_incoming_varargs (CUMULATIVE_ARGS *cum,
+ enum machine_mode mode ATTRIBUTE_UNUSED,
+ tree type ATTRIBUTE_UNUSED, int *pretend_size,
+ int no_rtl)
+{
+ rtx mem;
+ int i;
+
+ if (no_rtl)
+ return;
+
+ /* The move for named arguments will be generated automatically by the
+ compiler. We need to generate the move rtx for the unnamed arguments
+ if they are in the first 3 words. We assume atleast 1 named argument
+ exists, so we never generate [ARGP] = R0 here. */
+
+ for (i = cum->words + 1; i < max_arg_registers; i++)
+ {
+ mem = gen_rtx_MEM (Pmode,
+ plus_constant (arg_pointer_rtx, (i * UNITS_PER_WORD)));
+ emit_move_insn (mem, gen_rtx_REG (Pmode, i));
+ }
+
+ *pretend_size = 0;
+}
+
+/* Value should be nonzero if functions must have frame pointers.
+ Zero means the frame pointer need not be set up (and parms may
+ be accessed via the stack pointer) in functions that seem suitable. */
+
+int
+bfin_frame_pointer_required (void)
+{
+ e_funkind fkind = funkind (TREE_TYPE (current_function_decl));
+
+ if (fkind != SUBROUTINE)
+ return 1;
+
+ /* We turn on on -fomit-frame-pointer if -momit-leaf-frame-pointer is used,
+ so we have to override it for non-leaf functions. */
+ if (TARGET_OMIT_LEAF_FRAME_POINTER && ! current_function_is_leaf)
+ return 1;
+
+ return 0;
+}
+
+/* Return the number of registers pushed during the prologue. */
+
+static int
+n_regs_saved_by_prologue (void)
+{
+ e_funkind fkind = funkind (TREE_TYPE (current_function_decl));
+ int n = n_dregs_to_save () + n_pregs_to_save ();
+
+ if (stack_frame_needed_p ())
+ /* We use a LINK instruction in this case. */
+ n += 2;
+ else
+ {
+ if (must_save_fp_p ())
+ n++;
+ if (! current_function_is_leaf)
+ n++;
+ }
+
+ if (fkind != SUBROUTINE)
+ {
+ tree attrs = TYPE_ATTRIBUTES (TREE_TYPE (current_function_decl));
+ tree all = lookup_attribute ("saveall", attrs);
+ int i;
+
+ /* Increment once for ASTAT. */
+ n++;
+
+ /* RETE/X/N. */
+ if (lookup_attribute ("nesting", attrs))
+ n++;
+
+ for (i = REG_P7 + 1; i < REG_CC; i++)
+ if (all
+ || regs_ever_live[i]
+ || (!leaf_function_p () && call_used_regs[i]))
+ n += i == REG_A0 || i == REG_A1 ? 2 : 1;
+ }
+ return n;
+}
+
+/* Return the offset between two registers, one to be eliminated, and the other
+ its replacement, at the start of a routine. */
+
+HOST_WIDE_INT
+bfin_initial_elimination_offset (int from, int to)
+{
+ HOST_WIDE_INT offset = 0;
+
+ if (from == ARG_POINTER_REGNUM)
+ offset = n_regs_saved_by_prologue () * 4;
+
+ if (to == STACK_POINTER_REGNUM)
+ {
+ if (current_function_outgoing_args_size >= FIXED_STACK_AREA)
+ offset += current_function_outgoing_args_size;
+ else if (current_function_outgoing_args_size)
+ offset += FIXED_STACK_AREA;
+
+ offset += get_frame_size ();
+ }
+
+ return offset;
+}
+
+/* Emit code to load a constant CONSTANT into register REG; setting
+ RTX_FRAME_RELATED_P on all insns we generate. Make sure that the insns
+ we generate need not be split. */
+
+static void
+frame_related_constant_load (rtx reg, HOST_WIDE_INT constant)
+{
+ rtx insn;
+ rtx cst = GEN_INT (constant);
+
+ if (constant >= -32768 && constant < 65536)
+ insn = emit_move_insn (reg, cst);
+ else
+ {
+ /* We don't call split_load_immediate here, since dwarf2out.c can get
+ confused about some of the more clever sequences it can generate. */
+ insn = emit_insn (gen_movsi_high (reg, cst));
+ RTX_FRAME_RELATED_P (insn) = 1;
+ insn = emit_insn (gen_movsi_low (reg, reg, cst));
+ }
+ RTX_FRAME_RELATED_P (insn) = 1;
+}
+
+/* Generate efficient code to add a value to the frame pointer. We
+ can use P1 as a scratch register. Set RTX_FRAME_RELATED_P on the
+ generated insns if FRAME is nonzero. */
+
+static void
+add_to_sp (rtx spreg, HOST_WIDE_INT value, int frame)
+{
+ if (value == 0)
+ return;
+
+ /* Choose whether to use a sequence using a temporary register, or
+ a sequence with multiple adds. We can add a signed 7 bit value
+ in one instruction. */
+ if (value > 120 || value < -120)
+ {
+ rtx tmpreg = gen_rtx_REG (SImode, REG_P1);
+ rtx insn;
+
+ if (frame)
+ frame_related_constant_load (tmpreg, value);
+ else
+ {
+ insn = emit_move_insn (tmpreg, GEN_INT (value));
+ if (frame)
+ RTX_FRAME_RELATED_P (insn) = 1;
+ }
+
+ insn = emit_insn (gen_addsi3 (spreg, spreg, tmpreg));
+ if (frame)
+ RTX_FRAME_RELATED_P (insn) = 1;
+ }
+ else
+ do
+ {
+ int size = value;
+ rtx insn;
+
+ if (size > 60)
+ size = 60;
+ else if (size < -60)
+ /* We could use -62, but that would leave the stack unaligned, so
+ it's no good. */
+ size = -60;
+
+ insn = emit_insn (gen_addsi3 (spreg, spreg, GEN_INT (size)));
+ if (frame)
+ RTX_FRAME_RELATED_P (insn) = 1;
+ value -= size;
+ }
+ while (value != 0);
+}
+
+/* Generate a LINK insn for a frame sized FRAME_SIZE. If this constant
+ is too large, generate a sequence of insns that has the same effect.
+ SPREG contains (reg:SI REG_SP). */
+
+static void
+emit_link_insn (rtx spreg, HOST_WIDE_INT frame_size)
+{
+ HOST_WIDE_INT link_size = frame_size;
+ rtx insn;
+ int i;
+
+ if (link_size > 262140)
+ link_size = 262140;
+
+ /* Use a LINK insn with as big a constant as possible, then subtract
+ any remaining size from the SP. */
+ insn = emit_insn (gen_link (GEN_INT (-8 - link_size)));
+ RTX_FRAME_RELATED_P (insn) = 1;
+
+ for (i = 0; i < XVECLEN (PATTERN (insn), 0); i++)
+ {
+ rtx set = XVECEXP (PATTERN (insn), 0, i);
+ if (GET_CODE (set) != SET)
+ abort ();
+ RTX_FRAME_RELATED_P (set) = 1;
+ }
+
+ frame_size -= link_size;
+
+ if (frame_size > 0)
+ {
+ /* Must use a call-clobbered PREG that isn't the static chain. */
+ rtx tmpreg = gen_rtx_REG (Pmode, REG_P1);
+
+ frame_related_constant_load (tmpreg, -frame_size);
+ insn = emit_insn (gen_addsi3 (spreg, spreg, tmpreg));
+ RTX_FRAME_RELATED_P (insn) = 1;
+ }
+}
+
+/* Return the number of bytes we must reserve for outgoing arguments
+ in the current function's stack frame. */
+
+static HOST_WIDE_INT
+arg_area_size (void)
+{
+ if (current_function_outgoing_args_size)
+ {
+ if (current_function_outgoing_args_size >= FIXED_STACK_AREA)
+ return current_function_outgoing_args_size;
+ else
+ return FIXED_STACK_AREA;
+ }
+ return 0;
+}
+
+/* Save RETS and FP, and allocate a stack frame. */
+
+static void
+do_link (rtx spreg, HOST_WIDE_INT frame_size)
+{
+ frame_size += arg_area_size ();
+
+ if (stack_frame_needed_p ()
+ || (must_save_fp_p () && ! current_function_is_leaf))
+ emit_link_insn (spreg, frame_size);
+ else
+ {
+ if (! current_function_is_leaf)
+ {
+ rtx pat = gen_movsi (gen_rtx_MEM (Pmode,
+ gen_rtx_PRE_DEC (Pmode, spreg)),
+ bfin_rets_rtx);
+ rtx insn = emit_insn (pat);
+ RTX_FRAME_RELATED_P (insn) = 1;
+ }
+ if (must_save_fp_p ())
+ {
+ rtx pat = gen_movsi (gen_rtx_MEM (Pmode,
+ gen_rtx_PRE_DEC (Pmode, spreg)),
+ gen_rtx_REG (Pmode, REG_FP));
+ rtx insn = emit_insn (pat);
+ RTX_FRAME_RELATED_P (insn) = 1;
+ }
+ add_to_sp (spreg, -frame_size, 1);
+ }
+}
+
+/* Like do_link, but used for epilogues to deallocate the stack frame. */
+
+static void
+do_unlink (rtx spreg, HOST_WIDE_INT frame_size)
+{
+ frame_size += arg_area_size ();
+
+ if (stack_frame_needed_p ())
+ emit_insn (gen_unlink ());
+ else
+ {
+ rtx postinc = gen_rtx_MEM (Pmode, gen_rtx_POST_INC (Pmode, spreg));
+
+ add_to_sp (spreg, frame_size, 0);
+ if (must_save_fp_p ())
+ {
+ rtx fpreg = gen_rtx_REG (Pmode, REG_FP);
+ emit_move_insn (fpreg, postinc);
+ emit_insn (gen_rtx_USE (VOIDmode, fpreg));
+ }
+ if (! current_function_is_leaf)
+ {
+ emit_move_insn (bfin_rets_rtx, postinc);
+ emit_insn (gen_rtx_USE (VOIDmode, bfin_rets_rtx));
+ }
+ }
+}
+
+/* Generate a prologue suitable for a function of kind FKIND. This is
+ called for interrupt and exception handler prologues.
+ SPREG contains (reg:SI REG_SP). */
+
+static void
+expand_interrupt_handler_prologue (rtx spreg, e_funkind fkind)
+{
+ int i;
+ HOST_WIDE_INT frame_size = get_frame_size ();
+ rtx predec1 = gen_rtx_PRE_DEC (SImode, spreg);
+ rtx predec = gen_rtx_MEM (SImode, predec1);
+ rtx insn;
+ tree attrs = TYPE_ATTRIBUTES (TREE_TYPE (current_function_decl));
+ tree all = lookup_attribute ("saveall", attrs);
+ tree kspisusp = lookup_attribute ("kspisusp", attrs);
+
+ if (kspisusp)
+ {
+ insn = emit_move_insn (spreg, gen_rtx_REG (Pmode, REG_USP));
+ RTX_FRAME_RELATED_P (insn) = 1;
+ }
+
+ /* We need space on the stack in case we need to save the argument
+ registers. */
+ if (fkind == EXCPT_HANDLER)
+ {
+ insn = emit_insn (gen_addsi3 (spreg, spreg, GEN_INT (-12)));
+ RTX_FRAME_RELATED_P (insn) = 1;
+ }
+
+ insn = emit_move_insn (predec, gen_rtx_REG (SImode, REG_ASTAT));
+ RTX_FRAME_RELATED_P (insn) = 1;
+
+ expand_prologue_reg_save (spreg, all != NULL_TREE);
+
+ for (i = REG_P7 + 1; i < REG_CC; i++)
+ if (all
+ || regs_ever_live[i]
+ || (!leaf_function_p () && call_used_regs[i]))
+ {
+ if (i == REG_A0 || i == REG_A1)
+ insn = emit_move_insn (gen_rtx_MEM (PDImode, predec1),
+ gen_rtx_REG (PDImode, i));
+ else
+ insn = emit_move_insn (predec, gen_rtx_REG (SImode, i));
+ RTX_FRAME_RELATED_P (insn) = 1;
+ }
+
+ if (lookup_attribute ("nesting", attrs))
+ {
+ rtx srcreg = gen_rtx_REG (Pmode, (fkind == EXCPT_HANDLER ? REG_RETX
+ : fkind == NMI_HANDLER ? REG_RETN
+ : REG_RETI));
+ insn = emit_move_insn (predec, srcreg);
+ RTX_FRAME_RELATED_P (insn) = 1;
+ }
+
+ do_link (spreg, frame_size);
+
+ if (fkind == EXCPT_HANDLER)
+ {
+ rtx r0reg = gen_rtx_REG (SImode, REG_R0);
+ rtx r1reg = gen_rtx_REG (SImode, REG_R1);
+ rtx r2reg = gen_rtx_REG (SImode, REG_R2);
+ rtx insn;
+
+ insn = emit_move_insn (r0reg, gen_rtx_REG (SImode, REG_SEQSTAT));
+ REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_MAYBE_DEAD, const0_rtx,
+ NULL_RTX);
+ insn = emit_insn (gen_ashrsi3 (r0reg, r0reg, GEN_INT (26)));
+ REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_MAYBE_DEAD, const0_rtx,
+ NULL_RTX);
+ insn = emit_insn (gen_ashlsi3 (r0reg, r0reg, GEN_INT (26)));
+ REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_MAYBE_DEAD, const0_rtx,
+ NULL_RTX);
+ insn = emit_move_insn (r1reg, spreg);
+ REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_MAYBE_DEAD, const0_rtx,
+ NULL_RTX);
+ insn = emit_move_insn (r2reg, gen_rtx_REG (Pmode, REG_FP));
+ REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_MAYBE_DEAD, const0_rtx,
+ NULL_RTX);
+ insn = emit_insn (gen_addsi3 (r2reg, r2reg, GEN_INT (8)));
+ REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_MAYBE_DEAD, const0_rtx,
+ NULL_RTX);
+ }
+}
+
+/* Generate an epilogue suitable for a function of kind FKIND. This is
+ called for interrupt and exception handler epilogues.
+ SPREG contains (reg:SI REG_SP). */
+
+static void
+expand_interrupt_handler_epilogue (rtx spreg, e_funkind fkind)
+{
+ int i;
+ rtx postinc1 = gen_rtx_POST_INC (SImode, spreg);
+ rtx postinc = gen_rtx_MEM (SImode, postinc1);
+ tree attrs = TYPE_ATTRIBUTES (TREE_TYPE (current_function_decl));
+ tree all = lookup_attribute ("saveall", attrs);
+
+ /* A slightly crude technique to stop flow from trying to delete "dead"
+ insns. */
+ MEM_VOLATILE_P (postinc) = 1;
+
+ do_unlink (spreg, get_frame_size ());
+
+ if (lookup_attribute ("nesting", attrs))
+ {
+ rtx srcreg = gen_rtx_REG (Pmode, (fkind == EXCPT_HANDLER ? REG_RETX
+ : fkind == NMI_HANDLER ? REG_RETN
+ : REG_RETI));
+ emit_move_insn (srcreg, postinc);
+ }
+
+ for (i = REG_CC - 1; i > REG_P7; i--)
+ if (all
+ || regs_ever_live[i]
+ || (!leaf_function_p () && call_used_regs[i]))
+ {
+ if (i == REG_A0 || i == REG_A1)
+ {
+ rtx mem = gen_rtx_MEM (PDImode, postinc1);
+ MEM_VOLATILE_P (mem) = 1;
+ emit_move_insn (gen_rtx_REG (PDImode, i), mem);
+ }
+ else
+ emit_move_insn (gen_rtx_REG (SImode, i), postinc);
+ }
+
+ expand_epilogue_reg_restore (spreg, all != NULL_TREE);
+
+ emit_move_insn (gen_rtx_REG (SImode, REG_ASTAT), postinc);
+
+ /* Deallocate any space we left on the stack in case we needed to save the
+ argument registers. */
+ if (fkind == EXCPT_HANDLER)
+ emit_insn (gen_addsi3 (spreg, spreg, GEN_INT (12)));
+
+ emit_jump_insn (gen_return_internal (GEN_INT (fkind)));
+}
+
+/* Generate RTL for the prologue of the current function. */
+
+void
+bfin_expand_prologue (void)
+{
+ rtx insn;
+ HOST_WIDE_INT frame_size = get_frame_size ();
+ rtx spreg = gen_rtx_REG (Pmode, REG_SP);
+ e_funkind fkind = funkind (TREE_TYPE (current_function_decl));
+
+ if (fkind != SUBROUTINE)
+ {
+ expand_interrupt_handler_prologue (spreg, fkind);
+ return;
+ }
+
+ expand_prologue_reg_save (spreg, 0);
+
+ do_link (spreg, frame_size);
+
+ if (TARGET_ID_SHARED_LIBRARY
+ && (current_function_uses_pic_offset_table
+ || !current_function_is_leaf))
+ {
+ rtx addr;
+
+ if (bfin_library_id_string)
+ addr = plus_constant (pic_offset_table_rtx, atoi (bfin_library_id_string));
+ else
+ addr = gen_rtx_PLUS (Pmode, pic_offset_table_rtx,
+ gen_rtx_UNSPEC (Pmode, gen_rtvec (1, const0_rtx),
+ UNSPEC_LIBRARY_OFFSET));
+ insn = emit_insn (gen_movsi (pic_offset_table_rtx,
+ gen_rtx_MEM (Pmode, addr)));
+ REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_MAYBE_DEAD, const0_rtx, NULL);
+ }
+}
+
+/* Generate RTL for the epilogue of the current function. NEED_RETURN is zero
+ if this is for a sibcall. EH_RETURN is nonzero if we're expanding an
+ eh_return pattern. */
+
+void
+bfin_expand_epilogue (int need_return, int eh_return)
+{
+ rtx spreg = gen_rtx_REG (Pmode, REG_SP);
+ e_funkind fkind = funkind (TREE_TYPE (current_function_decl));
+
+ if (fkind != SUBROUTINE)
+ {
+ expand_interrupt_handler_epilogue (spreg, fkind);
+ return;
+ }
+
+ do_unlink (spreg, get_frame_size ());
+
+ expand_epilogue_reg_restore (spreg, 0);
+
+ /* Omit the return insn if this is for a sibcall. */
+ if (! need_return)
+ return;
+
+ if (eh_return)
+ emit_insn (gen_addsi3 (spreg, spreg, gen_rtx_REG (Pmode, REG_P2)));
+
+ emit_jump_insn (gen_return_internal (GEN_INT (SUBROUTINE)));
+}
+\f
+/* Return nonzero if register OLD_REG can be renamed to register NEW_REG. */
+
+int
+bfin_hard_regno_rename_ok (unsigned int old_reg ATTRIBUTE_UNUSED,
+ unsigned int new_reg)
+{
+ /* Interrupt functions can only use registers that have already been
+ saved by the prologue, even if they would normally be
+ call-clobbered. */
+
+ if (funkind (TREE_TYPE (current_function_decl)) != SUBROUTINE
+ && !regs_ever_live[new_reg])
+ return 0;
+
+ return 1;
+}
+
+/* Return the value of the return address for the frame COUNT steps up
+ from the current frame, after the prologue.
+ We punt for everything but the current frame by returning const0_rtx. */
+
+rtx
+bfin_return_addr_rtx (int count)
+{
+ if (count != 0)
+ return const0_rtx;
+
+ return get_hard_reg_initial_val (Pmode, REG_RETS);
+}
+
+/* Try machine-dependent ways of modifying an illegitimate address X
+ to be legitimate. If we find one, return the new, valid address,
+ otherwise return NULL_RTX.
+
+ OLDX is the address as it was before break_out_memory_refs was called.
+ In some cases it is useful to look at this to decide what needs to be done.
+
+ MODE is the mode of the memory reference. */
+
+rtx
+legitimize_address (rtx x ATTRIBUTE_UNUSED, rtx oldx ATTRIBUTE_UNUSED,
+ enum machine_mode mode ATTRIBUTE_UNUSED)
+{
+ return NULL_RTX;
+}
+
+/* This predicate is used to compute the length of a load/store insn.
+ OP is a MEM rtx, we return nonzero if its addressing mode requires a
+ 32 bit instruction. */
+
+int
+effective_address_32bit_p (rtx op, enum machine_mode mode)
+{
+ HOST_WIDE_INT offset;
+
+ mode = GET_MODE (op);
+ op = XEXP (op, 0);
+
+ if (REG_P (op) || GET_CODE (op) == POST_INC
+ || GET_CODE (op) == PRE_DEC || GET_CODE (op) == POST_DEC)
+ return 0;
+ if (GET_CODE (op) != PLUS)
+ abort ();
+
+ offset = INTVAL (XEXP (op, 1));
+
+ /* All byte loads use a 16 bit offset. */
+ if (GET_MODE_SIZE (mode) == 1)
+ return 1;
+
+ if (GET_MODE_SIZE (mode) == 4)
+ {
+ /* Frame pointer relative loads can use a negative offset, all others
+ are restricted to a small positive one. */
+ if (XEXP (op, 0) == frame_pointer_rtx)
+ return offset < -128 || offset > 60;
+ return offset < 0 || offset > 60;
+ }
+
+ /* Must be HImode now. */
+ return offset < 0 || offset > 30;
+}
+
+/* Return cost of the memory address ADDR.
+ All addressing modes are equally cheap on the Blackfin. */
+
+static int
+bfin_address_cost (rtx addr ATTRIBUTE_UNUSED)
+{
+ return 1;
+}
+
+/* Subroutine of print_operand; used to print a memory reference X to FILE. */
+
+void
+print_address_operand (FILE *file, rtx x)
+{
+ if (GET_CODE (x) == MEM)
+ abort ();
+
+ switch (GET_CODE (x))
+ {
+ case PLUS:
+ output_address (XEXP (x, 0));
+ fprintf (file, "+");
+ output_address (XEXP (x, 1));
+ break;
+
+ case PRE_DEC:
+ fprintf (file, "--");
+ output_address (XEXP (x, 0));
+ break;
+ case POST_INC:
+ output_address (XEXP (x, 0));
+ fprintf (file, "++");
+ break;
+ case POST_DEC:
+ output_address (XEXP (x, 0));
+ fprintf (file, "--");
+ break;
+
+ default:
+ print_operand (file, x, 0);
+ }
+}
+
+/* Adding intp DImode support by Tony
+ * -- Q: (low word)
+ * -- R: (high word)
+ */
+
+void
+print_operand (FILE *file, rtx x, char code)
+{
+ enum machine_mode mode = GET_MODE (x);
+
+ switch (code)
+ {
+ case 'j':
+ switch (GET_CODE (x))
+ {
+ case EQ:
+ fprintf (file, "e");
+ break;
+ case NE:
+ fprintf (file, "ne");
+ break;
+ case GT:
+ fprintf (file, "g");
+ break;
+ case LT:
+ fprintf (file, "l");
+ break;
+ case GE:
+ fprintf (file, "ge");
+ break;
+ case LE:
+ fprintf (file, "le");
+ break;
+ case GTU:
+ fprintf (file, "g");
+ break;
+ case LTU:
+ fprintf (file, "l");
+ break;
+ case GEU:
+ fprintf (file, "ge");
+ break;
+ case LEU:
+ fprintf (file, "le");
+ break;
+ default:
+ output_operand_lossage ("invalid %%j value");
+ }
+ break;
+
+ case 'J': /* reverse logic */
+ switch (GET_CODE(x))
+ {
+ case EQ:
+ fprintf (file, "ne");
+ break;
+ case NE:
+ fprintf (file, "e");
+ break;
+ case GT:
+ fprintf (file, "le");
+ break;
+ case LT:
+ fprintf (file, "ge");
+ break;
+ case GE:
+ fprintf (file, "l");
+ break;
+ case LE:
+ fprintf (file, "g");
+ break;
+ case GTU:
+ fprintf (file, "le");
+ break;
+ case LTU:
+ fprintf (file, "ge");
+ break;
+ case GEU:
+ fprintf (file, "l");
+ break;
+ case LEU:
+ fprintf (file, "g");
+ break;
+ default:
+ output_operand_lossage ("invalid %%J value");
+ }
+ break;
+
+ default:
+ switch (GET_CODE (x))
+ {
+ case REG:
+ if (code == 'h')
+ {
+ gcc_assert (REGNO (x) < 32);
+ fprintf (file, "%s", short_reg_names[REGNO (x)]);
+ /*fprintf (file, "\n%d\n ", REGNO (x));*/
+ break;
+ }
+ else if (code == 'd')
+ {
+ gcc_assert (REGNO (x) < 32);
+ fprintf (file, "%s", high_reg_names[REGNO (x)]);
+ break;
+ }
+ else if (code == 'w')
+ {
+ gcc_assert (REGNO (x) == REG_A0 || REGNO (x) == REG_A1);
+ fprintf (file, "%s.w", reg_names[REGNO (x)]);
+ }
+ else if (code == 'x')
+ {
+ gcc_assert (REGNO (x) == REG_A0 || REGNO (x) == REG_A1);
+ fprintf (file, "%s.x", reg_names[REGNO (x)]);
+ }
+ else if (code == 'D')
+ {
+ fprintf (file, "%s", dregs_pair_names[REGNO (x)]);
+ }
+ else if (code == 'H')
+ {
+ gcc_assert (mode == DImode || mode == DFmode);
+ gcc_assert (REG_P (x));
+ fprintf (file, "%s", reg_names[REGNO (x) + 1]);
+ }
+ else if (code == 'T')
+ {
+ if (REGNO (x) > 7)
+ abort ();
+ fprintf (file, "%s", byte_reg_names[REGNO (x)]);
+ }
+ else
+ fprintf (file, "%s", reg_names[REGNO (x)]);
+ break;
+
+ case MEM:
+ fputc ('[', file);
+ x = XEXP (x,0);
+ print_address_operand (file, x);
+ fputc (']', file);
+ break;
+
+ case CONST_INT:
+ /* Moves to half registers with d or h modifiers always use unsigned
+ constants. */
+ if (code == 'd')
+ x = GEN_INT ((INTVAL (x) >> 16) & 0xffff);
+ else if (code == 'h')
+ x = GEN_INT (INTVAL (x) & 0xffff);
+ else if (code == 'X')
+ x = GEN_INT (exact_log2 (0xffffffff & INTVAL (x)));
+ else if (code == 'Y')
+ x = GEN_INT (exact_log2 (0xffffffff & ~INTVAL (x)));
+ else if (code == 'Z')
+ /* Used for LINK insns. */
+ x = GEN_INT (-8 - INTVAL (x));
+
+ /* fall through */
+
+ case SYMBOL_REF:
+ output_addr_const (file, x);
+ if (code == 'G' && flag_pic)
+ fprintf (file, "@GOT");
+ break;
+
+ case CONST_DOUBLE:
+ output_operand_lossage ("invalid const_double operand");
+ break;
+
+ case UNSPEC:
+ if (XINT (x, 1) == UNSPEC_MOVE_PIC)
+ {
+ output_addr_const (file, XVECEXP (x, 0, 0));
+ fprintf (file, "@GOT");
+ }
+ else if (XINT (x, 1) == UNSPEC_LIBRARY_OFFSET)
+ fprintf (file, "_current_shared_library_p5_offset_");
+ else
+ abort ();
+ break;
+
+ default:
+ output_addr_const (file, x);
+ }
+ }
+}
+\f
+/* Argument support functions. */
+
+/* Initialize a variable CUM of type CUMULATIVE_ARGS
+ for a call to a function whose data type is FNTYPE.
+ For a library call, FNTYPE is 0.
+ VDSP C Compiler manual, our ABI says that
+ first 3 words of arguments will use R0, R1 and R2.
+*/
+
+void
+init_cumulative_args (CUMULATIVE_ARGS *cum, tree fntype ATTRIBUTE_UNUSED,
+ rtx libname ATTRIBUTE_UNUSED)
+{
+ static CUMULATIVE_ARGS zero_cum;
+
+ *cum = zero_cum;
+
+ /* Set up the number of registers to use for passing arguments. */
+
+ cum->nregs = max_arg_registers;
+ cum->arg_regs = arg_regs;
+
+ return;
+}
+
+/* Update the data in CUM to advance over an argument
+ of mode MODE and data type TYPE.
+ (TYPE is null for libcalls where that information may not be available.) */
+
+void
+function_arg_advance (CUMULATIVE_ARGS *cum, enum machine_mode mode, tree type,
+ int named ATTRIBUTE_UNUSED)
+{
+ int count, bytes, words;
+
+ bytes = (mode == BLKmode) ? int_size_in_bytes (type) : GET_MODE_SIZE (mode);
+ words = (bytes + UNITS_PER_WORD - 1) / UNITS_PER_WORD;
+
+ cum->words += words;
+ cum->nregs -= words;
+
+ if (cum->nregs <= 0)
+ {
+ cum->nregs = 0;
+ cum->arg_regs = NULL;
+ }
+ else
+ {
+ for (count = 1; count <= words; count++)
+ cum->arg_regs++;
+ }
+
+ return;
+}
+
+/* Define where to put the arguments to a function.
+ Value is zero to push the argument on the stack,
+ or a hard register in which to store the argument.
+
+ MODE is the argument's machine mode.
+ TYPE is the data type of the argument (as a tree).
+ This is null for libcalls where that information may
+ not be available.
+ CUM is a variable of type CUMULATIVE_ARGS which gives info about
+ the preceding args and about the function being called.
+ NAMED is nonzero if this argument is a named parameter
+ (otherwise it is an extra parameter matching an ellipsis). */
+
+struct rtx_def *
+function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode, tree type,
+ int named ATTRIBUTE_UNUSED)
+{
+ int bytes
+ = (mode == BLKmode) ? int_size_in_bytes (type) : GET_MODE_SIZE (mode);
+
+ if (bytes == -1)
+ return NULL_RTX;
+
+ if (cum->nregs)
+ return gen_rtx_REG (mode, *(cum->arg_regs));
+
+ return NULL_RTX;
+}
+
+/* For an arg passed partly in registers and partly in memory,
+ this is the number of bytes passed in registers.
+ For args passed entirely in registers or entirely in memory, zero.
+
+ Refer VDSP C Compiler manual, our ABI.
+ First 3 words are in registers. So, if a an argument is larger
+ than the registers available, it will span the register and
+ stack. */
+
+static int
+bfin_arg_partial_bytes (CUMULATIVE_ARGS *cum, enum machine_mode mode,
+ tree type ATTRIBUTE_UNUSED,
+ bool named ATTRIBUTE_UNUSED)
+{
+ int bytes
+ = (mode == BLKmode) ? int_size_in_bytes (type) : GET_MODE_SIZE (mode);
+ int bytes_left = cum->nregs * UNITS_PER_WORD;
+
+ if (bytes == -1)
+ return 0;
+
+ if (bytes_left == 0)
+ return 0;
+ if (bytes > bytes_left)
+ return bytes_left;
+ return 0;
+}
+
+/* Variable sized types are passed by reference. */
+
+static bool
+bfin_pass_by_reference (CUMULATIVE_ARGS *cum ATTRIBUTE_UNUSED,
+ enum machine_mode mode ATTRIBUTE_UNUSED,
+ tree type, bool named ATTRIBUTE_UNUSED)
+{
+ return type && TREE_CODE (TYPE_SIZE (type)) != INTEGER_CST;
+}
+
+/* Decide whether a type should be returned in memory (true)
+ or in a register (false). This is called by the macro
+ RETURN_IN_MEMORY. */
+
+int
+bfin_return_in_memory (tree type)
+{
+ int size;
+ enum machine_mode mode = TYPE_MODE (type);
+
+ if (mode == BLKmode)
+ return 1;
+ size = int_size_in_bytes (type);
+ if (VECTOR_MODE_P (mode) || mode == TImode)
+ {
+ /* User-created vectors small enough to fit in REG. */
+ if (size < 8)
+ return 0;
+ if (size == 8 || size == 16)
+ return 1;
+ }
+
+ if (size > 12)
+ return 1;
+ return 0;
+}
+
+/* Register in which address to store a structure value
+ is passed to a function. */
+static rtx
+bfin_struct_value_rtx (tree fntype ATTRIBUTE_UNUSED,
+ int incoming ATTRIBUTE_UNUSED)
+{
+ return gen_rtx_REG (Pmode, REG_P0);
+}
+
+/* Return true when register may be used to pass function parameters. */
+
+bool
+function_arg_regno_p (int n)
+{
+ int i;
+ for (i = 0; arg_regs[i] != -1; i++)
+ if (n == arg_regs[i])
+ return true;
+ return false;
+}
+
+/* Returns 1 if OP contains a symbol reference */
+
+int
+symbolic_reference_mentioned_p (rtx op)
+{
+ register const char *fmt;
+ register int i;
+
+ if (GET_CODE (op) == SYMBOL_REF || GET_CODE (op) == LABEL_REF)
+ return 1;
+
+ fmt = GET_RTX_FORMAT (GET_CODE (op));
+ for (i = GET_RTX_LENGTH (GET_CODE (op)) - 1; i >= 0; i--)
+ {
+ if (fmt[i] == 'E')
+ {
+ register int j;
+
+ for (j = XVECLEN (op, i) - 1; j >= 0; j--)
+ if (symbolic_reference_mentioned_p (XVECEXP (op, i, j)))
+ return 1;
+ }
+
+ else if (fmt[i] == 'e' && symbolic_reference_mentioned_p (XEXP (op, i)))
+ return 1;
+ }
+
+ return 0;
+}
+
+/* Decide whether we can make a sibling call to a function. DECL is the
+ declaration of the function being targeted by the call and EXP is the
+ CALL_EXPR representing the call. */
+
+static bool
+bfin_function_ok_for_sibcall (tree decl ATTRIBUTE_UNUSED,
+ tree exp ATTRIBUTE_UNUSED)
+{
+ return true;
+}
+\f
+/* Emit RTL insns to initialize the variable parts of a trampoline at
+ TRAMP. FNADDR is an RTX for the address of the function's pure
+ code. CXT is an RTX for the static chain value for the function. */
+
+void
+initialize_trampoline (tramp, fnaddr, cxt)
+ rtx tramp, fnaddr, cxt;
+{
+ rtx t1 = copy_to_reg (fnaddr);
+ rtx t2 = copy_to_reg (cxt);
+ rtx addr;
+
+ addr = memory_address (Pmode, plus_constant (tramp, 2));
+ emit_move_insn (gen_rtx_MEM (HImode, addr), gen_lowpart (HImode, t1));
+ emit_insn (gen_ashrsi3 (t1, t1, GEN_INT (16)));
+ addr = memory_address (Pmode, plus_constant (tramp, 6));
+ emit_move_insn (gen_rtx_MEM (HImode, addr), gen_lowpart (HImode, t1));
+
+ addr = memory_address (Pmode, plus_constant (tramp, 10));
+ emit_move_insn (gen_rtx_MEM (HImode, addr), gen_lowpart (HImode, t2));
+ emit_insn (gen_ashrsi3 (t2, t2, GEN_INT (16)));
+ addr = memory_address (Pmode, plus_constant (tramp, 14));
+ emit_move_insn (gen_rtx_MEM (HImode, addr), gen_lowpart (HImode, t2));
+}
+
+/* Legitimize PIC addresses. If the address is already position-independent,
+ we return ORIG. Newly generated position-independent addresses go into a
+ reg. This is REG if nonzero, otherwise we allocate register(s) as
+ necessary. */
+
+rtx
+legitimize_pic_address (rtx orig, rtx reg)
+{
+ rtx addr = orig;
+ rtx new = orig;
+
+ if (GET_CODE (addr) == SYMBOL_REF || GET_CODE (addr) == LABEL_REF)
+ {
+ if (GET_CODE (addr) == SYMBOL_REF && CONSTANT_POOL_ADDRESS_P (addr))
+ reg = new = orig;
+ else
+ {
+ if (reg == 0)
+ {
+ if (no_new_pseudos)
+ abort ();
+ reg = gen_reg_rtx (Pmode);
+ }
+
+ if (flag_pic == 2)
+ {
+ emit_insn (gen_movsi_high_pic (reg, addr));
+ emit_insn (gen_movsi_low_pic (reg, reg, addr));
+ emit_insn (gen_addsi3 (reg, reg, pic_offset_table_rtx));
+ new = gen_rtx_MEM (Pmode, reg);
+ }
+ else
+ {
+ rtx tmp = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr),
+ UNSPEC_MOVE_PIC);
+ new = gen_rtx_MEM (Pmode,
+ gen_rtx_PLUS (Pmode, pic_offset_table_rtx,
+ tmp));
+ }
+ emit_move_insn (reg, new);
+ }
+ current_function_uses_pic_offset_table = 1;
+ return reg;
+ }
+
+ else if (GET_CODE (addr) == CONST || GET_CODE (addr) == PLUS)
+ {
+ rtx base;
+
+ if (GET_CODE (addr) == CONST)
+ {
+ addr = XEXP (addr, 0);
+ if (GET_CODE (addr) != PLUS)
+ abort ();
+ }
+
+ if (XEXP (addr, 0) == pic_offset_table_rtx)
+ return orig;
+
+ if (reg == 0)
+ {
+ if (no_new_pseudos)
+ abort ();
+ reg = gen_reg_rtx (Pmode);
+ }
+
+ base = legitimize_pic_address (XEXP (addr, 0), reg);
+ addr = legitimize_pic_address (XEXP (addr, 1),
+ base == reg ? NULL_RTX : reg);
+
+ if (GET_CODE (addr) == CONST_INT)
+ {
+ if (! reload_in_progress && ! reload_completed)
+ addr = force_reg (Pmode, addr);
+ else
+ /* If we reach here, then something is seriously wrong. */
+ abort ();
+ }
+
+ if (GET_CODE (addr) == PLUS && CONSTANT_P (XEXP (addr, 1)))
+ {
+ base = gen_rtx_PLUS (Pmode, base, XEXP (addr, 0));
+ addr = XEXP (addr, 1);
+ }
+
+ return gen_rtx_PLUS (Pmode, base, addr);
+ }
+
+ return new;
+}
+
+/* Emit insns to move operands[1] into operands[0]. */
+
+void
+emit_pic_move (rtx *operands, enum machine_mode mode ATTRIBUTE_UNUSED)
+{
+ rtx temp = reload_in_progress ? operands[0] : gen_reg_rtx (Pmode);
+
+ if (GET_CODE (operands[0]) == MEM && SYMBOLIC_CONST (operands[1]))
+ operands[1] = force_reg (SImode, operands[1]);
+ else
+ operands[1] = legitimize_pic_address (operands[1], temp);
+}
+
+/* Expand a move operation in mode MODE. The operands are in OPERANDS. */
+
+void
+expand_move (rtx *operands, enum machine_mode mode)
+{
+ if (flag_pic && SYMBOLIC_CONST (operands[1]))
+ emit_pic_move (operands, mode);
+
+ /* Don't generate memory->memory or constant->memory moves, go through a
+ register */
+ else if ((reload_in_progress | reload_completed) == 0
+ && GET_CODE (operands[0]) == MEM
+ && GET_CODE (operands[1]) != REG)
+ operands[1] = force_reg (mode, operands[1]);
+}
+\f
+/* Split one or more DImode RTL references into pairs of SImode
+ references. The RTL can be REG, offsettable MEM, integer constant, or
+ CONST_DOUBLE. "operands" is a pointer to an array of DImode RTL to
+ split and "num" is its length. lo_half and hi_half are output arrays
+ that parallel "operands". */
+
+void
+split_di (rtx operands[], int num, rtx lo_half[], rtx hi_half[])
+{
+ while (num--)
+ {
+ rtx op = operands[num];
+
+ /* simplify_subreg refuse to split volatile memory addresses,
+ but we still have to handle it. */
+ if (GET_CODE (op) == MEM)
+ {
+ lo_half[num] = adjust_address (op, SImode, 0);
+ hi_half[num] = adjust_address (op, SImode, 4);
+ }
+ else
+ {
+ lo_half[num] = simplify_gen_subreg (SImode, op,
+ GET_MODE (op) == VOIDmode
+ ? DImode : GET_MODE (op), 0);
+ hi_half[num] = simplify_gen_subreg (SImode, op,
+ GET_MODE (op) == VOIDmode
+ ? DImode : GET_MODE (op), 4);
+ }
+ }
+}
+\f
+/* Expand a call instruction. FNADDR is the call target, RETVAL the return value.
+ SIBCALL is nonzero if this is a sibling call. */
+
+void
+bfin_expand_call (rtx retval, rtx fnaddr, rtx callarg1, int sibcall)
+{
+ rtx use = NULL, call;
+
+ /* Static functions and indirect calls don't need the pic register. */
+ if (flag_pic
+ && GET_CODE (XEXP (fnaddr, 0)) == SYMBOL_REF
+ && ! SYMBOL_REF_LOCAL_P (XEXP (fnaddr, 0)))
+ use_reg (&use, pic_offset_table_rtx);
+
+ if (! call_insn_operand (XEXP (fnaddr, 0), Pmode))
+ {
+ fnaddr = copy_to_mode_reg (Pmode, XEXP (fnaddr, 0));
+ fnaddr = gen_rtx_MEM (Pmode, fnaddr);
+ }
+ call = gen_rtx_CALL (VOIDmode, fnaddr, callarg1);
+
+ if (retval)
+ call = gen_rtx_SET (VOIDmode, retval, call);
+ if (sibcall)
+ {
+ rtx pat = gen_rtx_PARALLEL (VOIDmode, rtvec_alloc (2));
+ XVECEXP (pat, 0, 0) = call;
+ XVECEXP (pat, 0, 1) = gen_rtx_RETURN (VOIDmode);
+ call = pat;
+ }
+ call = emit_call_insn (call);
+ if (use)
+ CALL_INSN_FUNCTION_USAGE (call) = use;
+}
+\f
+/* Return 1 if hard register REGNO can hold a value of machine-mode MODE. */
+
+int
+hard_regno_mode_ok (int regno, enum machine_mode mode)
+{
+ /* Allow only dregs to store value of mode HI or QI */
+ enum reg_class class = REGNO_REG_CLASS (regno);
+
+ if (mode == CCmode)
+ return 0;
+
+ if (mode == V2HImode)
+ return D_REGNO_P (regno);
+ if (class == CCREGS)
+ return mode == BImode;
+ if (mode == PDImode)
+ return regno == REG_A0 || regno == REG_A1;
+ if (mode == SImode
+ && TEST_HARD_REG_BIT (reg_class_contents[PROLOGUE_REGS], regno))
+ return 1;
+
+ return TEST_HARD_REG_BIT (reg_class_contents[MOST_REGS], regno);
+}
+
+/* Implements target hook vector_mode_supported_p. */
+
+static bool
+bfin_vector_mode_supported_p (enum machine_mode mode)
+{
+ return mode == V2HImode;
+}
+
+/* Return the cost of moving data from a register in class CLASS1 to
+ one in class CLASS2. A cost of 2 is the default. */
+
+int
+bfin_register_move_cost (enum machine_mode mode ATTRIBUTE_UNUSED,
+ enum reg_class class1, enum reg_class class2)
+{
+ /* If optimizing for size, always prefer reg-reg over reg-memory moves. */
+ if (optimize_size)
+ return 2;
+
+ /* There are some stalls involved when moving from a DREG to a different
+ class reg, and using the value in one of the following instructions.
+ Attempt to model this by slightly discouraging such moves. */
+ if (class1 == DREGS && class2 != DREGS)
+ return 2 * 2;
+
+ return 2;
+}
+
+/* Return the cost of moving data of mode M between a
+ register and memory. A value of 2 is the default; this cost is
+ relative to those in `REGISTER_MOVE_COST'.
+
+ ??? In theory L1 memory has single-cycle latency. We should add a switch
+ that tells the compiler whether we expect to use only L1 memory for the
+ program; it'll make the costs more accurate. */
+
+int
+bfin_memory_move_cost (enum machine_mode mode ATTRIBUTE_UNUSED,
+ enum reg_class class,
+ int in ATTRIBUTE_UNUSED)
+{
+ /* Make memory accesses slightly more expensive than any register-register
+ move. Also, penalize non-DP registers, since they need secondary
+ reloads to load and store. */
+ if (! reg_class_subset_p (class, DPREGS))
+ return 10;
+
+ return 8;
+}
+
+/* Inform reload about cases where moving X with a mode MODE to a register in
+ CLASS requires an extra scratch register. Return the class needed for the
+ scratch register. */
+
+enum reg_class
+secondary_input_reload_class (enum reg_class class, enum machine_mode mode,
+ rtx x)
+{
+ /* If we have HImode or QImode, we can only use DREGS as secondary registers;
+ in most other cases we can also use PREGS. */
+ enum reg_class default_class = GET_MODE_SIZE (mode) >= 4 ? DPREGS : DREGS;
+ enum reg_class x_class = NO_REGS;
+ enum rtx_code code = GET_CODE (x);
+
+ if (code == SUBREG)
+ x = SUBREG_REG (x), code = GET_CODE (x);
+ if (REG_P (x))
+ {
+ int regno = REGNO (x);
+ if (regno >= FIRST_PSEUDO_REGISTER)
+ regno = reg_renumber[regno];
+
+ if (regno == -1)
+ code = MEM;
+ else
+ x_class = REGNO_REG_CLASS (regno);
+ }
+
+ /* We can be asked to reload (plus (FP) (large_constant)) into a DREG.
+ This happens as a side effect of register elimination, and we need
+ a scratch register to do it. */
+ if (fp_plus_const_operand (x, mode))
+ {
+ rtx op2 = XEXP (x, 1);
+ int large_constant_p = ! CONST_7BIT_IMM_P (INTVAL (op2));
+
+ if (class == PREGS || class == PREGS_CLOBBERED)
+ return NO_REGS;
+ /* If destination is a DREG, we can do this without a scratch register
+ if the constant is valid for an add instruction. */
+ if (class == DREGS || class == DPREGS)
+ return large_constant_p ? PREGS : NO_REGS;
+ /* Reloading to anything other than a DREG? Use a PREG scratch
+ register. */
+ return PREGS;
+ }
+
+ /* Data can usually be moved freely between registers of most classes.
+ AREGS are an exception; they can only move to or from another register
+ in AREGS or one in DREGS. They can also be assigned the constant 0. */
+ if (x_class == AREGS)
+ return class == DREGS || class == AREGS ? NO_REGS : DREGS;
+
+ if (class == AREGS)
+ {
+ if (x != const0_rtx && x_class != DREGS)
+ return DREGS;
+ else
+ return NO_REGS;
+ }
+
+ /* CCREGS can only be moved from/to DREGS. */
+ if (class == CCREGS && x_class != DREGS)
+ return DREGS;
+ if (x_class == CCREGS && class != DREGS)
+ return DREGS;
+ /* All registers other than AREGS can load arbitrary constants. The only
+ case that remains is MEM. */
+ if (code == MEM)
+ if (! reg_class_subset_p (class, default_class))
+ return default_class;
+ return NO_REGS;
+}
+
+/* Like secondary_input_reload_class; and all we do is call that function. */
+
+enum reg_class
+secondary_output_reload_class (enum reg_class class, enum machine_mode mode,
+ rtx x)
+{
+ return secondary_input_reload_class (class, mode, x);
+}
+\f
+/* Implement the macro OVERRIDE_OPTIONS. */
+
+void
+override_options (void)
+{
+ if (TARGET_OMIT_LEAF_FRAME_POINTER)
+ flag_omit_frame_pointer = 1;
+
+ /* Library identification */
+ if (bfin_library_id_string)
+ {
+ int id;
+
+ if (! TARGET_ID_SHARED_LIBRARY)
+ error ("-mshared-library-id= specified without -mid-shared-library");
+ id = atoi (bfin_library_id_string);
+ if (id < 0 || id > MAX_LIBRARY_ID)
+ error ("-mshared-library-id=%d is not between 0 and %d", id, MAX_LIBRARY_ID);
+
+ /* From now on, bfin_library_id_string will contain the library offset. */
+ asprintf ((char **)&bfin_library_id_string, "%d", (id * -4) - 4);
+ }
+
+ if (TARGET_ID_SHARED_LIBRARY)
+ /* ??? Provide a way to use a bigger GOT. */
+ flag_pic = 1;
+
+ flag_schedule_insns = 0;
+}
+
+/* Return the destination address of BRANCH. */
+
+static int
+branch_dest (rtx branch)
+{
+ rtx dest;
+ int dest_uid;
+ rtx pat = PATTERN (branch);
+ if (GET_CODE (pat) == PARALLEL)
+ pat = XVECEXP (pat, 0, 0);
+ dest = SET_SRC (pat);
+ if (GET_CODE (dest) == IF_THEN_ELSE)
+ dest = XEXP (dest, 1);
+ dest = XEXP (dest, 0);
+ dest_uid = INSN_UID (dest);
+ return INSN_ADDRESSES (dest_uid);
+}
+
+/* Return nonzero if INSN is annotated with a REG_BR_PROB note that indicates
+ it's a branch that's predicted taken. */
+
+static int
+cbranch_predicted_taken_p (rtx insn)
+{
+ rtx x = find_reg_note (insn, REG_BR_PROB, 0);
+
+ if (x)
+ {
+ int pred_val = INTVAL (XEXP (x, 0));
+
+ return pred_val >= REG_BR_PROB_BASE / 2;
+ }
+
+ return 0;
+}
+
+/* Templates for use by asm_conditional_branch. */
+
+static const char *ccbranch_templates[][3] = {
+ { "if !cc jump %3;", "if cc jump 4 (bp); jump.s %3;", "if cc jump 6 (bp); jump.l %3;" },
+ { "if cc jump %3;", "if !cc jump 4 (bp); jump.s %3;", "if !cc jump 6 (bp); jump.l %3;" },
+ { "if !cc jump %3 (bp);", "if cc jump 4; jump.s %3;", "if cc jump 6; jump.l %3;" },
+ { "if cc jump %3 (bp);", "if !cc jump 4; jump.s %3;", "if !cc jump 6; jump.l %3;" },
+};
+
+/* Output INSN, which is a conditional branch instruction with operands
+ OPERANDS.
+
+ We deal with the various forms of conditional branches that can be generated
+ by bfin_reorg to prevent the hardware from doing speculative loads, by
+ - emitting a sufficient number of nops, if N_NOPS is nonzero, or
+ - always emitting the branch as predicted taken, if PREDICT_TAKEN is true.
+ Either of these is only necessary if the branch is short, otherwise the
+ template we use ends in an unconditional jump which flushes the pipeline
+ anyway. */
+
+void
+asm_conditional_branch (rtx insn, rtx *operands, int n_nops, int predict_taken)
+{
+ int offset = branch_dest (insn) - INSN_ADDRESSES (INSN_UID (insn));
+ /* Note : offset for instructions like if cc jmp; jump.[sl] offset
+ is to be taken from start of if cc rather than jump.
+ Range for jump.s is (-4094, 4096) instead of (-4096, 4094)
+ */
+ int len = (offset >= -1024 && offset <= 1022 ? 0
+ : offset >= -4094 && offset <= 4096 ? 1
+ : 2);
+ int bp = predict_taken && len == 0 ? 1 : cbranch_predicted_taken_p (insn);
+ int idx = (bp << 1) | (GET_CODE (operands[0]) == EQ ? BRF : BRT);
+ output_asm_insn (ccbranch_templates[idx][len], operands);
+ if (n_nops > 0 && bp)
+ abort ();
+ if (len == 0)
+ while (n_nops-- > 0)
+ output_asm_insn ("nop;", NULL);
+}
+
+/* Emit rtl for a comparison operation CMP in mode MODE. Operands have been
+ stored in bfin_compare_op0 and bfin_compare_op1 already. */
+
+rtx
+bfin_gen_compare (rtx cmp, enum machine_mode mode ATTRIBUTE_UNUSED)
+{
+ enum rtx_code code1, code2;
+ rtx op0 = bfin_compare_op0, op1 = bfin_compare_op1;
+ rtx tem = bfin_cc_rtx;
+ enum rtx_code code = GET_CODE (cmp);
+
+ /* If we have a BImode input, then we already have a compare result, and
+ do not need to emit another comparison. */
+ if (GET_MODE (op0) == BImode)
+ {
+ if ((code == NE || code == EQ) && op1 == const0_rtx)
+ tem = op0, code2 = code;
+ else
+ abort ();
+ }
+ else
+ {
+ switch (code) {
+ /* bfin has these conditions */
+ case EQ:
+ case LT:
+ case LE:
+ case LEU:
+ case LTU:
+ code1 = code;
+ code2 = NE;
+ break;
+ default:
+ code1 = reverse_condition (code);
+ code2 = EQ;
+ break;
+ }
+ emit_insn (gen_rtx_SET (BImode, tem,
+ gen_rtx_fmt_ee (code1, BImode, op0, op1)));
+ }
+
+ return gen_rtx_fmt_ee (code2, BImode, tem, CONST0_RTX (BImode));
+}
+\f
+/* Return nonzero iff C has exactly one bit set if it is interpreted
+ as a 32 bit constant. */
+
+int
+log2constp (unsigned HOST_WIDE_INT c)
+{
+ c &= 0xFFFFFFFF;
+ return c != 0 && (c & (c-1)) == 0;
+}
+
+/* Returns the number of consecutive least significant zeros in the binary
+ representation of *V.
+ We modify *V to contain the original value arithmetically shifted right by
+ the number of zeroes. */
+
+static int
+shiftr_zero (HOST_WIDE_INT *v)
+{
+ unsigned HOST_WIDE_INT tmp = *v;
+ unsigned HOST_WIDE_INT sgn;
+ int n = 0;
+
+ if (tmp == 0)
+ return 0;
+
+ sgn = tmp & ((unsigned HOST_WIDE_INT) 1 << (HOST_BITS_PER_WIDE_INT - 1));
+ while ((tmp & 0x1) == 0 && n <= 32)
+ {
+ tmp = (tmp >> 1) | sgn;
+ n++;
+ }
+ *v = tmp;
+ return n;
+}
+
+/* After reload, split the load of an immediate constant. OPERANDS are the
+ operands of the movsi_insn pattern which we are splitting. We return
+ nonzero if we emitted a sequence to load the constant, zero if we emitted
+ nothing because we want to use the splitter's default sequence. */
+
+int
+split_load_immediate (rtx operands[])
+{
+ HOST_WIDE_INT val = INTVAL (operands[1]);
+ HOST_WIDE_INT tmp;
+ HOST_WIDE_INT shifted = val;
+ HOST_WIDE_INT shifted_compl = ~val;
+ int num_zero = shiftr_zero (&shifted);
+ int num_compl_zero = shiftr_zero (&shifted_compl);
+ unsigned int regno = REGNO (operands[0]);
+ enum reg_class class1 = REGNO_REG_CLASS (regno);
+
+ /* This case takes care of single-bit set/clear constants, which we could
+ also implement with BITSET/BITCLR. */
+ if (num_zero
+ && shifted >= -32768 && shifted < 65536
+ && (D_REGNO_P (regno)
+ || (regno >= REG_P0 && regno <= REG_P7 && num_zero <= 2)))
+ {
+ emit_insn (gen_movsi (operands[0], GEN_INT (shifted)));
+ emit_insn (gen_ashlsi3 (operands[0], operands[0], GEN_INT (num_zero)));
+ return 1;
+ }
+
+ tmp = val & 0xFFFF;
+ tmp |= -(tmp & 0x8000);
+
+ /* If high word has one bit set or clear, try to use a bit operation. */
+ if (D_REGNO_P (regno))
+ {
+ if (log2constp (val & 0xFFFF0000))
+ {
+ emit_insn (gen_movsi (operands[0], GEN_INT (val & 0xFFFF)));
+ emit_insn (gen_iorsi3 (operands[0], operands[0], GEN_INT (val & 0xFFFF0000)));
+ return 1;
+ }
+ else if (log2constp (val | 0xFFFF) && (val & 0x8000) != 0)
+ {
+ emit_insn (gen_movsi (operands[0], GEN_INT (tmp)));
+ emit_insn (gen_andsi3 (operands[0], operands[0], GEN_INT (val | 0xFFFF)));
+ }
+ }
+
+ if (D_REGNO_P (regno))
+ {
+ if (CONST_7BIT_IMM_P (tmp))
+ {
+ emit_insn (gen_movsi (operands[0], GEN_INT (tmp)));
+ emit_insn (gen_movstricthi_high (operands[0], GEN_INT (val & -65536)));
+ return 1;
+ }
+
+ if ((val & 0xFFFF0000) == 0)
+ {
+ emit_insn (gen_movsi (operands[0], const0_rtx));
+ emit_insn (gen_movsi_low (operands[0], operands[0], operands[1]));
+ return 1;
+ }
+
+ if ((val & 0xFFFF0000) == 0xFFFF0000)
+ {
+ emit_insn (gen_movsi (operands[0], constm1_rtx));
+ emit_insn (gen_movsi_low (operands[0], operands[0], operands[1]));
+ return 1;
+ }
+ }
+
+ /* Need DREGs for the remaining case. */
+ if (regno > REG_R7)
+ return 0;
+
+ if (optimize_size
+ && num_compl_zero && CONST_7BIT_IMM_P (shifted_compl))
+ {
+ /* If optimizing for size, generate a sequence that has more instructions
+ but is shorter. */
+ emit_insn (gen_movsi (operands[0], GEN_INT (shifted_compl)));
+ emit_insn (gen_ashlsi3 (operands[0], operands[0],
+ GEN_INT (num_compl_zero)));
+ emit_insn (gen_one_cmplsi2 (operands[0], operands[0]));
+ return 1;
+ }
+ return 0;
+}
+\f
+/* Return true if the legitimate memory address for a memory operand of mode
+ MODE. Return false if not. */
+
+static bool
+bfin_valid_add (enum machine_mode mode, HOST_WIDE_INT value)
+{
+ unsigned HOST_WIDE_INT v = value > 0 ? value : -value;
+ int sz = GET_MODE_SIZE (mode);
+ int shift = sz == 1 ? 0 : sz == 2 ? 1 : 2;
+ /* The usual offsettable_memref machinery doesn't work so well for this
+ port, so we deal with the problem here. */
+ unsigned HOST_WIDE_INT mask = sz == 8 ? 0x7ffe : 0x7fff;
+ return (v & ~(mask << shift)) == 0;
+}
+
+static bool
+bfin_valid_reg_p (unsigned int regno, int strict)
+{
+ return ((strict && REGNO_OK_FOR_BASE_STRICT_P (regno))
+ || (!strict && REGNO_OK_FOR_BASE_NONSTRICT_P (regno)));
+}
+
+bool
+bfin_legitimate_address_p (enum machine_mode mode, rtx x, int strict)
+{
+ switch (GET_CODE (x)) {
+ case REG:
+ if (bfin_valid_reg_p (REGNO (x), strict))
+ return true;
+ break;
+ case PLUS:
+ if (REG_P (XEXP (x, 0))
+ && bfin_valid_reg_p (REGNO (XEXP (x, 0)), strict)
+ && (GET_CODE (XEXP (x, 1)) == UNSPEC
+ || (GET_CODE (XEXP (x, 1)) == CONST_INT
+ && bfin_valid_add (mode, INTVAL (XEXP (x, 1))))))
+ return true;
+ break;
+ case POST_INC:
+ case POST_DEC:
+ if (LEGITIMATE_MODE_FOR_AUTOINC_P (mode)
+ && REG_P (XEXP (x, 0))
+ && bfin_valid_reg_p (REGNO (XEXP (x, 0)), strict))
+ return true;
+ case PRE_DEC:
+ if (LEGITIMATE_MODE_FOR_AUTOINC_P (mode)
+ && XEXP (x, 0) == stack_pointer_rtx
+ && REG_P (XEXP (x, 0))
+ && bfin_valid_reg_p (REGNO (XEXP (x, 0)), strict))
+ return true;
+ break;
+ default:
+ break;
+ }
+ return false;
+}
+
+static bool
+bfin_rtx_costs (rtx x, int code, int outer_code, int *total)
+{
+ int cost2 = COSTS_N_INSNS (1);
+
+ switch (code)
+ {
+ case CONST_INT:
+ if (outer_code == SET || outer_code == PLUS)
+ *total = CONST_7BIT_IMM_P (INTVAL (x)) ? 0 : cost2;
+ else if (outer_code == AND)
+ *total = log2constp (~INTVAL (x)) ? 0 : cost2;
+ else if (outer_code == LE || outer_code == LT || outer_code == EQ)
+ *total = (INTVAL (x) >= -4 && INTVAL (x) <= 3) ? 0 : cost2;
+ else if (outer_code == LEU || outer_code == LTU)
+ *total = (INTVAL (x) >= 0 && INTVAL (x) <= 7) ? 0 : cost2;
+ else if (outer_code == MULT)
+ *total = (INTVAL (x) == 2 || INTVAL (x) == 4) ? 0 : cost2;
+ else if (outer_code == ASHIFT && (INTVAL (x) == 1 || INTVAL (x) == 2))
+ *total = 0;
+ else if (outer_code == ASHIFT || outer_code == ASHIFTRT
+ || outer_code == LSHIFTRT)
+ *total = (INTVAL (x) >= 0 && INTVAL (x) <= 31) ? 0 : cost2;
+ else if (outer_code == IOR || outer_code == XOR)
+ *total = (INTVAL (x) & (INTVAL (x) - 1)) == 0 ? 0 : cost2;
+ else
+ *total = cost2;
+ return true;
+
+ case CONST:
+ case LABEL_REF:
+ case SYMBOL_REF:
+ case CONST_DOUBLE:
+ *total = COSTS_N_INSNS (2);
+ return true;
+
+ case PLUS:
+ if (GET_MODE (x) == Pmode)
+ {
+ if (GET_CODE (XEXP (x, 0)) == MULT
+ && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT)
+ {
+ HOST_WIDE_INT val = INTVAL (XEXP (XEXP (x, 0), 1));
+ if (val == 2 || val == 4)
+ {
+ *total = cost2;
+ *total += rtx_cost (XEXP (XEXP (x, 0), 0), outer_code);
+ *total += rtx_cost (XEXP (x, 1), outer_code);
+ return true;
+ }
+ }
+ }
+
+ /* fall through */
+
+ case MINUS:
+ case ASHIFT:
+ case ASHIFTRT:
+ case LSHIFTRT:
+ if (GET_MODE (x) == DImode)
+ *total = 6 * cost2;
+ return false;
+
+ case AND:
+ case IOR:
+ case XOR:
+ if (GET_MODE (x) == DImode)
+ *total = 2 * cost2;
+ return false;
+
+ case MULT:
+ if (GET_MODE_SIZE (GET_MODE (x)) <= UNITS_PER_WORD)
+ *total = COSTS_N_INSNS (3);
+ return false;
+
+ default:
+ return false;
+ }
+}
+
+static void
+bfin_internal_label (FILE *stream, const char *prefix, unsigned long num)
+{
+ fprintf (stream, "%s%s$%ld:\n", LOCAL_LABEL_PREFIX, prefix, num);
+}
+\f
+/* Used for communication between {push,pop}_multiple_operation (which
+ we use not only as a predicate) and the corresponding output functions. */
+static int first_preg_to_save, first_dreg_to_save;
+
+int
+push_multiple_operation (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
+{
+ int lastdreg = 8, lastpreg = 6;
+ int i, group;
+
+ first_preg_to_save = lastpreg;
+ first_dreg_to_save = lastdreg;
+ for (i = 1, group = 0; i < XVECLEN (op, 0) - 1; i++)
+ {
+ rtx t = XVECEXP (op, 0, i);
+ rtx src, dest;
+ int regno;
+
+ if (GET_CODE (t) != SET)
+ return 0;
+
+ src = SET_SRC (t);
+ dest = SET_DEST (t);
+ if (GET_CODE (dest) != MEM || ! REG_P (src))
+ return 0;
+ dest = XEXP (dest, 0);
+ if (GET_CODE (dest) != PLUS
+ || ! REG_P (XEXP (dest, 0))
+ || REGNO (XEXP (dest, 0)) != REG_SP
+ || GET_CODE (XEXP (dest, 1)) != CONST_INT
+ || INTVAL (XEXP (dest, 1)) != -i * 4)
+ return 0;
+
+ regno = REGNO (src);
+ if (group == 0)
+ {
+ if (D_REGNO_P (regno))
+ {
+ group = 1;
+ first_dreg_to_save = lastdreg = regno - REG_R0;
+ }
+ else if (regno >= REG_P0 && regno <= REG_P7)
+ {
+ group = 2;
+ first_preg_to_save = lastpreg = regno - REG_P0;
+ }
+ else
+ return 0;
+
+ continue;
+ }
+
+ if (group == 1)
+ {
+ if (regno >= REG_P0 && regno <= REG_P7)
+ {
+ group = 2;
+ first_preg_to_save = lastpreg = regno - REG_P0;
+ }
+ else if (regno != REG_R0 + lastdreg + 1)
+ return 0;
+ else
+ lastdreg++;
+ }
+ else if (group == 2)
+ {
+ if (regno != REG_P0 + lastpreg + 1)
+ return 0;
+ lastpreg++;
+ }
+ }
+ return 1;
+}
+
+int
+pop_multiple_operation (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
+{
+ int lastdreg = 8, lastpreg = 6;
+ int i, group;
+
+ for (i = 1, group = 0; i < XVECLEN (op, 0); i++)
+ {
+ rtx t = XVECEXP (op, 0, i);
+ rtx src, dest;
+ int regno;
+
+ if (GET_CODE (t) != SET)
+ return 0;
+
+ src = SET_SRC (t);
+ dest = SET_DEST (t);
+ if (GET_CODE (src) != MEM || ! REG_P (dest))
+ return 0;
+ src = XEXP (src, 0);
+
+ if (i == 1)
+ {
+ if (! REG_P (src) || REGNO (src) != REG_SP)
+ return 0;
+ }
+ else if (GET_CODE (src) != PLUS
+ || ! REG_P (XEXP (src, 0))
+ || REGNO (XEXP (src, 0)) != REG_SP
+ || GET_CODE (XEXP (src, 1)) != CONST_INT
+ || INTVAL (XEXP (src, 1)) != (i - 1) * 4)
+ return 0;
+
+ regno = REGNO (dest);
+ if (group == 0)
+ {
+ if (regno == REG_R7)
+ {
+ group = 1;
+ lastdreg = 7;
+ }
+ else if (regno != REG_P0 + lastpreg - 1)
+ return 0;
+ else
+ lastpreg--;
+ }
+ else if (group == 1)
+ {
+ if (regno != REG_R0 + lastdreg - 1)
+ return 0;
+ else
+ lastdreg--;
+ }
+ }
+ first_dreg_to_save = lastdreg;
+ first_preg_to_save = lastpreg;
+ return 1;
+}
+
+/* Emit assembly code for one multi-register push described by INSN, with
+ operands in OPERANDS. */
+
+void
+output_push_multiple (rtx insn, rtx *operands)
+{
+ char buf[80];
+ /* Validate the insn again, and compute first_[dp]reg_to_save. */
+ if (! push_multiple_operation (PATTERN (insn), VOIDmode))
+ abort ();
+ if (first_dreg_to_save == 8)
+ sprintf (buf, "[--sp] = ( p5:%d );\n", first_preg_to_save);
+ else if (first_preg_to_save == 6)
+ sprintf (buf, "[--sp] = ( r7:%d );\n", first_dreg_to_save);
+ else
+ sprintf (buf, "[--sp] = ( r7:%d, p5:%d );\n", first_dreg_to_save, first_preg_to_save);
+
+ output_asm_insn (buf, operands);
+}
+
+/* Emit assembly code for one multi-register pop described by INSN, with
+ operands in OPERANDS. */
+
+void
+output_pop_multiple (rtx insn, rtx *operands)
+{
+ char buf[80];
+ /* Validate the insn again, and compute first_[dp]reg_to_save. */
+ if (! pop_multiple_operation (PATTERN (insn), VOIDmode))
+ abort ();
+
+ if (first_dreg_to_save == 8)
+ sprintf (buf, "( p5:%d ) = [sp++];\n", first_preg_to_save);
+ else if (first_preg_to_save == 6)
+ sprintf (buf, "( r7:%d ) = [sp++];\n", first_dreg_to_save);
+ else
+ sprintf (buf, "( r7:%d, p5:%d ) = [sp++];\n", first_dreg_to_save, first_preg_to_save);
+
+ output_asm_insn (buf, operands);
+}
+
+/* Adjust DST and SRC by OFFSET bytes, and generate one move in mode MODE. */
+
+static void
+single_move_for_strmov (rtx dst, rtx src, enum machine_mode mode, HOST_WIDE_INT offset)
+{
+ rtx scratch = gen_reg_rtx (mode);
+ rtx srcmem, dstmem;
+
+ srcmem = adjust_address_nv (src, mode, offset);
+ dstmem = adjust_address_nv (dst, mode, offset);
+ emit_move_insn (scratch, srcmem);
+ emit_move_insn (dstmem, scratch);
+}
+
+/* Expand a string move operation of COUNT_EXP bytes from SRC to DST, with
+ alignment ALIGN_EXP. Return true if successful, false if we should fall
+ back on a different method. */
+
+bool
+bfin_expand_strmov (rtx dst, rtx src, rtx count_exp, rtx align_exp)
+{
+ rtx srcreg, destreg, countreg;
+ HOST_WIDE_INT align = 0;
+ unsigned HOST_WIDE_INT count = 0;
+
+ if (GET_CODE (align_exp) == CONST_INT)
+ align = INTVAL (align_exp);
+ if (GET_CODE (count_exp) == CONST_INT)
+ {
+ count = INTVAL (count_exp);
+#if 0
+ if (!TARGET_INLINE_ALL_STRINGOPS && count > 64)
+ return false;
+#endif
+ }
+
+ /* If optimizing for size, only do single copies inline. */
+ if (optimize_size)
+ {
+ if (count == 2 && align < 2)
+ return false;
+ if (count == 4 && align < 4)
+ return false;
+ if (count != 1 && count != 2 && count != 4)
+ return false;
+ }
+ if (align < 2 && count != 1)
+ return false;
+
+ destreg = copy_to_mode_reg (Pmode, XEXP (dst, 0));
+ if (destreg != XEXP (dst, 0))
+ dst = replace_equiv_address_nv (dst, destreg);
+ srcreg = copy_to_mode_reg (Pmode, XEXP (src, 0));
+ if (srcreg != XEXP (src, 0))
+ src = replace_equiv_address_nv (src, srcreg);
+
+ if (count != 0 && align >= 2)
+ {
+ unsigned HOST_WIDE_INT offset = 0;
+
+ if (align >= 4)
+ {
+ if ((count & ~3) == 4)
+ {
+ single_move_for_strmov (dst, src, SImode, offset);
+ offset = 4;
+ }
+ else if (count & ~3)
+ {
+ HOST_WIDE_INT new_count = ((count >> 2) & 0x3fffffff) - 1;
+ countreg = copy_to_mode_reg (Pmode, GEN_INT (new_count));
+
+ emit_insn (gen_rep_movsi (destreg, srcreg, countreg, destreg, srcreg));
+ }
+ }
+ else
+ {
+ if ((count & ~1) == 2)
+ {
+ single_move_for_strmov (dst, src, HImode, offset);
+ offset = 2;
+ }
+ else if (count & ~1)
+ {
+ HOST_WIDE_INT new_count = ((count >> 1) & 0x7fffffff) - 1;
+ countreg = copy_to_mode_reg (Pmode, GEN_INT (new_count));
+
+ emit_insn (gen_rep_movhi (destreg, srcreg, countreg, destreg, srcreg));
+ }
+ }
+ if (count & 2)
+ {
+ single_move_for_strmov (dst, src, HImode, offset);
+ offset += 2;
+ }
+ if (count & 1)
+ {
+ single_move_for_strmov (dst, src, QImode, offset);
+ }
+ return true;
+ }
+ return false;
+}
+
+\f
+static int
+bfin_adjust_cost (rtx insn, rtx link, rtx dep_insn, int cost)
+{
+ enum attr_type insn_type, dep_insn_type;
+ int dep_insn_code_number;
+
+ /* Anti and output dependencies have zero cost. */
+ if (REG_NOTE_KIND (link) != 0)
+ return 0;
+
+ dep_insn_code_number = recog_memoized (dep_insn);
+
+ /* If we can't recognize the insns, we can't really do anything. */
+ if (dep_insn_code_number < 0 || recog_memoized (insn) < 0)
+ return cost;
+
+ insn_type = get_attr_type (insn);
+ dep_insn_type = get_attr_type (dep_insn);
+
+ if (dep_insn_type == TYPE_MOVE || dep_insn_type == TYPE_MCLD)
+ {
+ rtx pat = PATTERN (dep_insn);
+ rtx dest = SET_DEST (pat);
+ rtx src = SET_SRC (pat);
+ if (! ADDRESS_REGNO_P (REGNO (dest)) || ! D_REGNO_P (REGNO (src)))
+ return cost;
+ return cost + (dep_insn_type == TYPE_MOVE ? 4 : 3);
+ }
+
+ return cost;
+}
+\f
+/* We use the machine specific reorg pass for emitting CSYNC instructions
+ after conditional branches as needed.
+
+ The Blackfin is unusual in that a code sequence like
+ if cc jump label
+ r0 = (p0)
+ may speculatively perform the load even if the condition isn't true. This
+ happens for a branch that is predicted not taken, because the pipeline
+ isn't flushed or stalled, so the early stages of the following instructions,
+ which perform the memory reference, are allowed to execute before the
+ jump condition is evaluated.
+ Therefore, we must insert additional instructions in all places where this
+ could lead to incorrect behaviour. The manual recommends CSYNC, while
+ VDSP seems to use NOPs (even though its corresponding compiler option is
+ named CSYNC).
+
+ When optimizing for speed, we emit NOPs, which seems faster than a CSYNC.
+ When optimizing for size, we turn the branch into a predicted taken one.
+ This may be slower due to mispredicts, but saves code size. */
+
+static void
+bfin_reorg (void)
+{
+ rtx insn, last_condjump = NULL_RTX;
+ int cycles_since_jump = INT_MAX;
+
+ if (! TARGET_CSYNC)
+ return;
+
+ for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
+ {
+ rtx pat;
+
+ if (NOTE_P (insn) || BARRIER_P (insn) || LABEL_P (insn))
+ continue;
+
+ pat = PATTERN (insn);
+ if (GET_CODE (pat) == USE || GET_CODE (pat) == CLOBBER
+ || GET_CODE (pat) == ASM_INPUT || GET_CODE (pat) == ADDR_VEC
+ || GET_CODE (pat) == ADDR_DIFF_VEC || asm_noperands (pat) >= 0)
+ continue;
+
+ if (JUMP_P (insn))
+ {
+ if (any_condjump_p (insn)
+ && ! cbranch_predicted_taken_p (insn))
+ {
+ last_condjump = insn;
+ cycles_since_jump = 0;
+ }
+ else
+ cycles_since_jump = INT_MAX;
+ }
+ else if (INSN_P (insn))
+ {
+ enum attr_type type = get_attr_type (insn);
+ if (cycles_since_jump < INT_MAX)
+ cycles_since_jump++;
+
+ if (type == TYPE_MCLD && cycles_since_jump < 3)
+ {
+ rtx pat;
+
+ pat = single_set (insn);
+ if (may_trap_p (SET_SRC (pat)))
+ {
+ int num_clobbers;
+ rtx *op = recog_data.operand;
+
+ extract_insn (last_condjump);
+ if (optimize_size)
+ pat = gen_cbranch_predicted_taken (op[0], op[1], op[2],
+ op[3]);
+ else
+ pat = gen_cbranch_with_nops (op[0], op[1], op[2], op[3],
+ GEN_INT (3 - cycles_since_jump));
+ PATTERN (last_condjump) = pat;
+ INSN_CODE (last_condjump) = recog (pat, insn, &num_clobbers);
+ cycles_since_jump = INT_MAX;
+ }
+ }
+ }
+ }
+}
+\f
+/* Handle interrupt_handler, exception_handler and nmi_handler function
+ attributes; arguments as in struct attribute_spec.handler. */
+
+static tree
+handle_int_attribute (tree *node, tree name,
+ tree args ATTRIBUTE_UNUSED,
+ int flags ATTRIBUTE_UNUSED,
+ bool *no_add_attrs)
+{
+ tree x = *node;
+ if (TREE_CODE (x) == FUNCTION_DECL)
+ x = TREE_TYPE (x);
+
+ if (TREE_CODE (x) != FUNCTION_TYPE)
+ {
+ warning ("%qs attribute only applies to functions",
+ IDENTIFIER_POINTER (name));
+ *no_add_attrs = true;
+ }
+ else if (funkind (x) != SUBROUTINE)
+ error ("multiple function type attributes specified");
+
+ return NULL_TREE;
+}
+
+/* Return 0 if the attributes for two types are incompatible, 1 if they
+ are compatible, and 2 if they are nearly compatible (which causes a
+ warning to be generated). */
+
+static int
+bfin_comp_type_attributes (tree type1, tree type2)
+{
+ e_funkind kind1, kind2;
+
+ if (TREE_CODE (type1) != FUNCTION_TYPE)
+ return 1;
+
+ kind1 = funkind (type1);
+ kind2 = funkind (type2);
+
+ if (kind1 != kind2)
+ return 0;
+
+ /* Check for mismatched modifiers */
+ if (!lookup_attribute ("nesting", TYPE_ATTRIBUTES (type1))
+ != !lookup_attribute ("nesting", TYPE_ATTRIBUTES (type2)))
+ return 0;
+
+ if (!lookup_attribute ("saveall", TYPE_ATTRIBUTES (type1))
+ != !lookup_attribute ("saveall", TYPE_ATTRIBUTES (type2)))
+ return 0;
+
+ if (!lookup_attribute ("kspisusp", TYPE_ATTRIBUTES (type1))
+ != !lookup_attribute ("kspisusp", TYPE_ATTRIBUTES (type2)))
+ return 0;
+
+ return 1;
+}
+
+/* Table of valid machine attributes. */
+const struct attribute_spec bfin_attribute_table[] =
+{
+ /* { name, min_len, max_len, decl_req, type_req, fn_type_req, handler } */
+ { "interrupt_handler", 0, 0, false, true, true, handle_int_attribute },
+ { "exception_handler", 0, 0, false, true, true, handle_int_attribute },
+ { "nmi_handler", 0, 0, false, true, true, handle_int_attribute },
+ { "nesting", 0, 0, false, true, true, NULL },
+ { "kspisusp", 0, 0, false, true, true, NULL },
+ { "saveall", 0, 0, false, true, true, NULL },
+ { NULL, 0, 0, false, false, false, NULL }
+};
+\f
+/* Output the assembler code for a thunk function. THUNK_DECL is the
+ declaration for the thunk function itself, FUNCTION is the decl for
+ the target function. DELTA is an immediate constant offset to be
+ added to THIS. If VCALL_OFFSET is nonzero, the word at
+ *(*this + vcall_offset) should be added to THIS. */
+
+static void
+bfin_output_mi_thunk (FILE *file ATTRIBUTE_UNUSED,
+ tree thunk ATTRIBUTE_UNUSED, HOST_WIDE_INT delta,
+ HOST_WIDE_INT vcall_offset, tree function)
+{
+ rtx xops[3];
+ /* The this parameter is passed as the first argument. */
+ rtx this = gen_rtx_REG (Pmode, REG_R0);
+
+ /* Adjust the this parameter by a fixed constant. */
+ if (delta)
+ {
+ xops[1] = this;
+ if (delta >= -64 && delta <= 63)
+ {
+ xops[0] = GEN_INT (delta);
+ output_asm_insn ("%1 += %0;", xops);
+ }
+ else if (delta >= -128 && delta < -64)
+ {
+ xops[0] = GEN_INT (delta + 64);
+ output_asm_insn ("%1 += -64; %1 += %0;", xops);
+ }
+ else if (delta > 63 && delta <= 126)
+ {
+ xops[0] = GEN_INT (delta - 63);
+ output_asm_insn ("%1 += 63; %1 += %0;", xops);
+ }
+ else
+ {
+ xops[0] = GEN_INT (delta);
+ output_asm_insn ("r3.l = %h0; r3.h = %d0; %1 = %1 + r3;", xops);
+ }
+ }
+
+ /* Adjust the this parameter by a value stored in the vtable. */
+ if (vcall_offset)
+ {
+ rtx p2tmp = gen_rtx_REG (Pmode, REG_P2);
+ rtx tmp = gen_rtx_REG (Pmode, REG_R2);
+
+ xops[1] = tmp;
+ xops[2] = p2tmp;
+ output_asm_insn ("%2 = r0; %2 = [%2];", xops);
+
+ /* Adjust the this parameter. */
+ xops[0] = gen_rtx_MEM (Pmode, plus_constant (p2tmp, vcall_offset));
+ if (!memory_operand (xops[0], Pmode))
+ {
+ rtx tmp2 = gen_rtx_REG (Pmode, REG_P1);
+ xops[0] = GEN_INT (vcall_offset);
+ xops[1] = tmp2;
+ output_asm_insn ("%h1 = %h0; %d1 = %d0; %2 = %2 + %1", xops);
+ xops[0] = gen_rtx_MEM (Pmode, p2tmp);
+ }
+ xops[2] = this;
+ output_asm_insn ("%1 = %0; %2 = %2 + %1;", xops);
+ }
+
+ xops[0] = XEXP (DECL_RTL (function), 0);
+ if (1 || !flag_pic || (*targetm.binds_local_p) (function))
+ output_asm_insn ("jump.l\t%P0", xops);
+}
+\f
+#undef TARGET_ASM_GLOBALIZE_LABEL
+#define TARGET_ASM_GLOBALIZE_LABEL bfin_globalize_label
+
+#undef TARGET_ASM_FILE_START
+#define TARGET_ASM_FILE_START output_file_start
+
+#undef TARGET_ATTRIBUTE_TABLE
+#define TARGET_ATTRIBUTE_TABLE bfin_attribute_table
+
+#undef TARGET_COMP_TYPE_ATTRIBUTES
+#define TARGET_COMP_TYPE_ATTRIBUTES bfin_comp_type_attributes
+
+#undef TARGET_RTX_COSTS
+#define TARGET_RTX_COSTS bfin_rtx_costs
+
+#undef TARGET_ADDRESS_COST
+#define TARGET_ADDRESS_COST bfin_address_cost
+
+#undef TARGET_ASM_INTERNAL_LABEL
+#define TARGET_ASM_INTERNAL_LABEL bfin_internal_label
+
+#undef TARGET_MACHINE_DEPENDENT_REORG
+#define TARGET_MACHINE_DEPENDENT_REORG bfin_reorg
+
+#undef TARGET_FUNCTION_OK_FOR_SIBCALL
+#define TARGET_FUNCTION_OK_FOR_SIBCALL bfin_function_ok_for_sibcall
+
+#undef TARGET_ASM_OUTPUT_MI_THUNK
+#define TARGET_ASM_OUTPUT_MI_THUNK bfin_output_mi_thunk
+#undef TARGET_ASM_CAN_OUTPUT_MI_THUNK
+#define TARGET_ASM_CAN_OUTPUT_MI_THUNK hook_bool_tree_hwi_hwi_tree_true
+
+#undef TARGET_SCHED_ADJUST_COST
+#define TARGET_SCHED_ADJUST_COST bfin_adjust_cost
+
+#undef TARGET_PROMOTE_PROTOTYPES
+#define TARGET_PROMOTE_PROTOTYPES hook_bool_tree_true
+#undef TARGET_PROMOTE_FUNCTION_ARGS
+#define TARGET_PROMOTE_FUNCTION_ARGS hook_bool_tree_true
+#undef TARGET_PROMOTE_FUNCTION_RETURN
+#define TARGET_PROMOTE_FUNCTION_RETURN hook_bool_tree_true
+
+#undef TARGET_ARG_PARTIAL_BYTES
+#define TARGET_ARG_PARTIAL_BYTES bfin_arg_partial_bytes
+
+#undef TARGET_PASS_BY_REFERENCE
+#define TARGET_PASS_BY_REFERENCE bfin_pass_by_reference
+
+#undef TARGET_SETUP_INCOMING_VARARGS
+#define TARGET_SETUP_INCOMING_VARARGS setup_incoming_varargs
+
+#undef TARGET_STRUCT_VALUE_RTX
+#define TARGET_STRUCT_VALUE_RTX bfin_struct_value_rtx
+
+#undef TARGET_VECTOR_MODE_SUPPORTED_P
+#define TARGET_VECTOR_MODE_SUPPORTED_P bfin_vector_mode_supported_p
+
+struct gcc_target targetm = TARGET_INITIALIZER;
--- /dev/null
+/* Definitions for the Blackfin port.
+ Copyright (C) 2005 Free Software Foundation, Inc.
+ Contributed by Analog Devices.
+
+ 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 2, 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 COPYING. If not, write to
+ the Free Software Foundation, 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA. */
+
+#ifndef _BFIN_CONFIG
+#define _BFIN_CONFIG
+
+#define OBJECT_FORMAT_ELF
+
+#define BRT 1
+#define BRF 0
+
+/* Print subsidiary information on the compiler version in use. */
+#define TARGET_VERSION fprintf (stderr, " (BlackFin bfin)")
+
+/* Run-time compilation parameters selecting different hardware subsets. */
+
+extern int target_flags;
+
+/* Predefinition in the preprocessor for this target machine */
+#ifndef TARGET_CPU_CPP_BUILTINS
+#define TARGET_CPU_CPP_BUILTINS() \
+ do \
+ { \
+ builtin_define ("bfin"); \
+ builtin_define ("BFIN"); \
+ } \
+ while (0)
+#endif
+
+/* Generate DSP instructions, like DSP halfword loads */
+#define TARGET_DSP (1)
+
+#define TARGET_DEFAULT MASK_CSYNC
+
+/* This macro is similar to `TARGET_SWITCHES' but defines names of
+ command options that have values. Its definition is an
+ initializer with a subgrouping for each command option.
+
+ Each subgrouping contains a string constant, that defines the
+ fixed part of the option name, and the address of a variable. The
+ variable, type `char *', is set to the variable part of the given
+ option if the fixed part matches. The actual option name is made
+ by appending `-m' to the specified name. */
+#define TARGET_OPTIONS \
+{ { "shared-library-id=", &bfin_library_id_string, \
+ "ID of shared library to build", 0} \
+}
+
+/* Maximum number of library ids we permit */
+#define MAX_LIBRARY_ID 255
+
+extern const char *bfin_library_id_string;
+
+/* Sometimes certain combinations of command options do not make
+ sense on a particular target machine. You can define a macro
+ `OVERRIDE_OPTIONS' to take account of this. This macro, if
+ defined, is executed once just after all the command options have
+ been parsed.
+
+ Don't use this macro to turn on various extra optimizations for
+ `-O'. That is what `OPTIMIZATION_OPTIONS' is for. */
+
+#define OVERRIDE_OPTIONS override_options ()
+
+#define FUNCTION_MODE SImode
+#define Pmode SImode
+
+/* store-condition-codes instructions store 0 for false
+ This is the value stored for true. */
+#define STORE_FLAG_VALUE 1
+
+/* Define this if pushing a word on the stack
+ makes the stack pointer a smaller address. */
+#define STACK_GROWS_DOWNWARD
+
+#define STACK_PUSH_CODE PRE_DEC
+
+/* Define this if the nominal address of the stack frame
+ is at the high-address end of the local variables;
+ that is, each additional local variable allocated
+ goes at a more negative offset in the frame. */
+#define FRAME_GROWS_DOWNWARD
+
+/* We define a dummy ARGP register; the parameters start at offset 0 from
+ it. */
+#define FIRST_PARM_OFFSET(DECL) 0
+
+/* Offset within stack frame to start allocating local variables at.
+ If FRAME_GROWS_DOWNWARD, this is the offset to the END of the
+ first local allocated. Otherwise, it is the offset to the BEGINNING
+ of the first local allocated. */
+#define STARTING_FRAME_OFFSET 0
+
+/* Register to use for pushing function arguments. */
+#define STACK_POINTER_REGNUM REG_P6
+
+/* Base register for access to local variables of the function. */
+#define FRAME_POINTER_REGNUM REG_P7
+
+/* A dummy register that will be eliminated to either FP or SP. */
+#define ARG_POINTER_REGNUM REG_ARGP
+
+/* `PIC_OFFSET_TABLE_REGNUM'
+ The register number of the register used to address a table of
+ static data addresses in memory. In some cases this register is
+ defined by a processor's "application binary interface" (ABI).
+ When this macro is defined, RTL is generated for this register
+ once, as with the stack pointer and frame pointer registers. If
+ this macro is not defined, it is up to the machine-dependent files
+ to allocate such a register (if necessary). */
+#define PIC_OFFSET_TABLE_REGNUM (REG_P5)
+
+/* A static chain register for nested functions. We need to use a
+ call-clobbered register for this. */
+#define STATIC_CHAIN_REGNUM REG_P2
+
+/* Define this if functions should assume that stack space has been
+ allocated for arguments even when their values are passed in
+ registers.
+
+ The value of this macro is the size, in bytes, of the area reserved for
+ arguments passed in registers.
+
+ This space can either be allocated by the caller or be a part of the
+ machine-dependent stack frame: `OUTGOING_REG_PARM_STACK_SPACE'
+ says which. */
+#define FIXED_STACK_AREA 12
+#define REG_PARM_STACK_SPACE(FNDECL) FIXED_STACK_AREA
+
+/* Define this if the above stack space is to be considered part of the
+ * space allocated by the caller. */
+#define OUTGOING_REG_PARM_STACK_SPACE
+
+/* Define this if the maximum size of all the outgoing args is to be
+ accumulated and pushed during the prologue. The amount can be
+ found in the variable current_function_outgoing_args_size. */
+#define ACCUMULATE_OUTGOING_ARGS 1
+
+/* Value should be nonzero if functions must have frame pointers.
+ Zero means the frame pointer need not be set up (and parms
+ may be accessed via the stack pointer) in functions that seem suitable.
+ This is computed in `reload', in reload1.c.
+*/
+#define FRAME_POINTER_REQUIRED (bfin_frame_pointer_required ())
+
+#define PARM_BOUNDRY 32
+
+#define STACK_BOUNDRY 32
+
+/*#define DATA_ALIGNMENT(TYPE, BASIC-ALIGN) for arrays.. */
+
+/* Make strings word-aligned so strcpy from constants will be faster. */
+#define CONSTANT_ALIGNMENT(EXP, ALIGN) \
+ (TREE_CODE (EXP) == STRING_CST \
+ && (ALIGN) < BITS_PER_WORD ? BITS_PER_WORD : (ALIGN))
+
+#define TRAMPOLINE_SIZE 18
+#define TRAMPOLINE_TEMPLATE(FILE) \
+ fprintf(FILE, "\t.dd\t0x0000e109\n"); /* p1.l = fn low */ \
+ fprintf(FILE, "\t.dd\t0x0000e149\n"); /* p1.h = fn high */; \
+ fprintf(FILE, "\t.dd\t0x0000e10a\n"); /* p2.l = sc low */; \
+ fprintf(FILE, "\t.dd\t0x0000e14a\n"); /* p2.h = sc high */; \
+ fprintf(FILE, "\t.dw\t0x0051\n"); /* jump (p1)*/
+
+#define INITIALIZE_TRAMPOLINE(TRAMP, FNADDR, CXT) \
+ initialize_trampoline (TRAMP, FNADDR, CXT)
+\f
+/* Definitions for register eliminations.
+
+ This is an array of structures. Each structure initializes one pair
+ of eliminable registers. The "from" register number is given first,
+ followed by "to". Eliminations of the same "from" register are listed
+ in order of preference.
+
+ There are two registers that can always be eliminated on the i386.
+ The frame pointer and the arg pointer can be replaced by either the
+ hard frame pointer or to the stack pointer, depending upon the
+ circumstances. The hard frame pointer is not used before reload and
+ so it is not eligible for elimination. */
+
+#define ELIMINABLE_REGS \
+{{ ARG_POINTER_REGNUM, STACK_POINTER_REGNUM}, \
+ { ARG_POINTER_REGNUM, FRAME_POINTER_REGNUM}, \
+ { FRAME_POINTER_REGNUM, STACK_POINTER_REGNUM}} \
+
+/* Given FROM and TO register numbers, say whether this elimination is
+ allowed. Frame pointer elimination is automatically handled.
+
+ All other eliminations are valid. */
+
+#define CAN_ELIMINATE(FROM, TO) \
+ ((TO) == STACK_POINTER_REGNUM ? ! frame_pointer_needed : 1)
+
+/* Define the offset between two registers, one to be eliminated, and the other
+ its replacement, at the start of a routine. */
+
+#define INITIAL_ELIMINATION_OFFSET(FROM, TO, OFFSET) \
+ ((OFFSET) = bfin_initial_elimination_offset ((FROM), (TO)))
+\f
+/* This processor has
+ 8 data register for doing arithmetic
+ 8 pointer register for doing addressing, including
+ 1 stack pointer P6
+ 1 frame pointer P7
+ 4 sets of indexing registers (I0-3, B0-3, L0-3, M0-3)
+ 1 condition code flag register CC
+ 5 return address registers RETS/I/X/N/E
+ 1 arithmetic status register (ASTAT). */
+
+#define FIRST_PSEUDO_REGISTER 44
+
+#define PREG_P(X) (REG_P (X) && REGNO (X) >= REG_P0 && REGNO (X) <= REG_P7)
+#define ADDRESS_REGNO_P(X) ((X) >= REG_P0 && (X) <= REG_M3)
+#define D_REGNO_P(X) ((X) <= REG_R7)
+
+#define REGISTER_NAMES { \
+ "R0", "R1", "R2", "R3", "R4", "R5", "R6", "R7", \
+ "P0", "P1", "P2", "P3", "P4", "P5", "SP", "FP", \
+ "I0", "B0", "L0", "I1", "B1", "L1", "I2", "B2", \
+ "L2", "I3", "B3", "L3", "M0", "M1", "M2", "M3", \
+ "A0", "A1", \
+ "CC", \
+ "RETS", "RETI", "RETX", "RETN", "RETE", "ASTAT", "SEQSTAT", "USP", \
+ "ARGP" \
+}
+
+#define SHORT_REGISTER_NAMES { \
+ "R0.L", "R1.L", "R2.L", "R3.L", "R4.L", "R5.L", "R6.L", "R7.L", \
+ "P0.L", "P1.L", "P2.L", "P3.L", "P4.L", "P5.L", "SP.L", "FP.L", \
+ "I0.L", "B0.L", "L0.L", "I1.L", "B1.L", "L1.L", "I2.L", "B2.L", \
+ "L2.L", "I3.L", "B3.L", "L3.L", "M0.L", "M1.L", "M2.L", "M3.L", }
+
+#define HIGH_REGISTER_NAMES { \
+ "R0.H", "R1.H", "R2.H", "R3.H", "R4.H", "R5.H", "R6.H", "R7.H", \
+ "P0.H", "P1.H", "P2.H", "P3.H", "P4.H", "P5.H", "SP.H", "FP.H", \
+ "I0.H", "B0.H", "L0.H", "I1.H", "B1.H", "L1.H", "I2.H", "B2.H", \
+ "L2.H", "I3.H", "B3.H", "L3.H", "M0.H", "M1.H", "M2.H", "M3.H", }
+
+#define DREGS_PAIR_NAMES { \
+ "R1:0.p", 0, "R3:2.p", 0, "R5:4.p", 0, "R7:6.p", 0, }
+
+#define BYTE_REGISTER_NAMES { \
+ "R0.B", "R1.B", "R2.B", "R3.B", "R4.B", "R5.B", "R6.B", "R7.B", }
+
+
+/* 1 for registers that have pervasive standard uses
+ and are not available for the register allocator. */
+
+#define FIXED_REGISTERS \
+/*r0 r1 r2 r3 r4 r5 r6 r7 p0 p1 p2 p3 p4 p5 p6 p7 */ \
+{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, \
+/*i0 b0 l0 i1 b1 l1 i2 b2 l2 i3 b3 l3 m0 m1 m2 m3 */ \
+ 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, \
+/*a0 a1 cc rets/i/x/n/e astat seqstat usp argp */ \
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 \
+}
+
+/* 1 for registers not available across function calls.
+ These must include the FIXED_REGISTERS and also any
+ registers that can be used without being saved.
+ The latter must include the registers where values are returned
+ and the register where structure-value addresses are passed.
+ Aside from that, you can include as many other registers as you like. */
+
+#define CALL_USED_REGISTERS \
+/*r0 r1 r2 r3 r4 r5 r6 r7 p0 p1 p2 p3 p4 p5 p6 p7 */ \
+{ 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, \
+/*i0 b0 l0 i1 b1 l1 i2 b2 l2 i3 b3 l3 m0 m1 m2 m3 */ \
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, \
+/*a0 a1 cc rets/i/x/n/e astat seqstat usp argp */ \
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 \
+}
+
+/* Order in which to allocate registers. Each register must be
+ listed once, even those in FIXED_REGISTERS. List frame pointer
+ late and fixed registers last. Note that, in general, we prefer
+ registers listed in CALL_USED_REGISTERS, keeping the others
+ available for storage of persistent values. */
+
+#define REG_ALLOC_ORDER \
+{ REG_R0, REG_R1, REG_R2, REG_R3, REG_R7, REG_R6, REG_R5, REG_R4, \
+ REG_P2, REG_P1, REG_P0, REG_P5, REG_P4, REG_P3, REG_P6, REG_P7, \
+ REG_A0, REG_A1, \
+ REG_I0, REG_B0, REG_L0, REG_I1, REG_B1, REG_L1, REG_I2, REG_B2, \
+ REG_L2, REG_I3, REG_B3, REG_L3, REG_M0, REG_M1, REG_M2, REG_M3, \
+ REG_RETS, REG_RETI, REG_RETX, REG_RETN, REG_RETE, \
+ REG_ASTAT, REG_SEQSTAT, REG_USP, \
+ REG_CC, REG_ARGP \
+}
+
+/* Macro to conditionally modify fixed_regs/call_used_regs. */
+#define CONDITIONAL_REGISTER_USAGE \
+ { \
+ conditional_register_usage(); \
+ if (flag_pic) \
+ { \
+ fixed_regs[PIC_OFFSET_TABLE_REGNUM] = 1; \
+ call_used_regs[PIC_OFFSET_TABLE_REGNUM] = 1; \
+ } \
+ }
+
+/* Define the classes of registers for register constraints in the
+ machine description. Also define ranges of constants.
+
+ One of the classes must always be named ALL_REGS and include all hard regs.
+ If there is more than one class, another class must be named NO_REGS
+ and contain no registers.
+
+ The name GENERAL_REGS must be the name of a class (or an alias for
+ another name such as ALL_REGS). This is the class of registers
+ that is allowed by "g" or "r" in a register constraint.
+ Also, registers outside this class are allocated only when
+ instructions express preferences for them.
+
+ The classes must be numbered in nondecreasing order; that is,
+ a larger-numbered class must never be contained completely
+ in a smaller-numbered class.
+
+ For any two classes, it is very desirable that there be another
+ class that represents their union. */
+
+
+enum reg_class
+{
+ NO_REGS,
+ IREGS,
+ BREGS,
+ LREGS,
+ MREGS,
+ CIRCREGS, /* Circular buffering registers, Ix, Bx, Lx together form. See Automatic Circlur Buffering */
+ DAGREGS,
+ EVEN_AREGS,
+ ODD_AREGS,
+ AREGS,
+ CCREGS,
+ EVEN_DREGS,
+ ODD_DREGS,
+ DREGS,
+ PREGS_CLOBBERED,
+ PREGS,
+ DPREGS,
+ MOST_REGS,
+ PROLOGUE_REGS,
+ NON_A_CC_REGS,
+ ALL_REGS, LIM_REG_CLASSES
+};
+
+#define N_REG_CLASSES ((int)LIM_REG_CLASSES)
+
+#define GENERAL_REGS DPREGS
+
+/* Give names of register classes as strings for dump file. */
+
+#define REG_CLASS_NAMES \
+{ "NO_REGS", \
+ "IREGS", \
+ "BREGS", \
+ "LREGS", \
+ "MREGS", \
+ "CIRCREGS", \
+ "DAGREGS", \
+ "EVEN_AREGS", \
+ "ODD_AREGS", \
+ "AREGS", \
+ "CCREGS", \
+ "EVEN_DREGS", \
+ "ODD_DREGS", \
+ "DREGS", \
+ "PREGS_CLOBBERED", \
+ "PREGS", \
+ "DPREGS", \
+ "MOST_REGS", \
+ "PROLOGUE_REGS", \
+ "NON_A_CC_REGS", \
+ "ALL_REGS" }
+
+/* An initializer containing the contents of the register classes, as integers
+ which are bit masks. The Nth integer specifies the contents of class N.
+ The way the integer MASK is interpreted is that register R is in the class
+ if `MASK & (1 << R)' is 1.
+
+ When the machine has more than 32 registers, an integer does not suffice.
+ Then the integers are replaced by sub-initializers, braced groupings
+ containing several integers. Each sub-initializer must be suitable as an
+ initializer for the type `HARD_REG_SET' which is defined in
+ `hard-reg-set.h'. */
+
+/* NOTE: DSP registers, IREGS - AREGS, are not GENERAL_REGS. We use
+ MOST_REGS as the union of DPREGS and DAGREGS. */
+
+#define REG_CLASS_CONTENTS \
+ /* 31 - 0 63-32 */ \
+{ { 0x00000000, 0 }, /* NO_REGS */ \
+ { 0x02490000, 0 }, /* IREGS */ \
+ { 0x04920000, 0 }, /* BREGS */ \
+ { 0x09240000, 0 }, /* LREGS */ \
+ { 0xf0000000, 0 }, /* MREGS */ \
+ { 0x0fff0000, 0 }, /* CIRCREGS */ \
+ { 0xffff0000, 0 }, /* DAGREGS */ \
+ { 0x00000000, 0x1 }, /* EVEN_AREGS */ \
+ { 0x00000000, 0x2 }, /* ODD_AREGS */ \
+ { 0x00000000, 0x3 }, /* AREGS */ \
+ { 0x00000000, 0x4 }, /* CCREGS */ \
+ { 0x00000055, 0 }, /* EVEN_DREGS */ \
+ { 0x000000aa, 0 }, /* ODD_DREGS */ \
+ { 0x000000ff, 0 }, /* DREGS */ \
+ { 0x00004700, 0x800 }, /* PREGS_CLOBBERED */ \
+ { 0x0000ff00, 0x800 }, /* PREGS */ \
+ { 0x0000ffff, 0x800 }, /* DPREGS */ \
+ { 0xffffffff, 0x800 }, /* MOST_REGS */\
+ { 0x00000000, 0x7f8 }, /* PROLOGUE_REGS */\
+ { 0xffffffff, 0xff8 }, /* NON_A_CC_REGS */\
+ { 0xffffffff, 0xfff }} /* ALL_REGS */
+
+#define BASE_REG_CLASS PREGS
+#define INDEX_REG_CLASS PREGS
+
+#define REGNO_OK_FOR_BASE_STRICT_P(X) (REGNO_REG_CLASS (X) == BASE_REG_CLASS)
+#define REGNO_OK_FOR_BASE_NONSTRICT_P(X) \
+ (((X) >= FIRST_PSEUDO_REGISTER) || REGNO_REG_CLASS (X) == BASE_REG_CLASS)
+
+#ifdef REG_OK_STRICT
+#define REGNO_OK_FOR_BASE_P(X) REGNO_OK_FOR_BASE_STRICT_P (X)
+#else
+#define REGNO_OK_FOR_BASE_P(X) REGNO_OK_FOR_BASE_NONSTRICT_P (X)
+#endif
+
+#define REG_OK_FOR_BASE_P(X) (REG_P (X) && REGNO_OK_FOR_BASE_P (REGNO (X)))
+#define REG_OK_FOR_INDEX_P(X) 0
+#define REGNO_OK_FOR_INDEX_P(X) 0
+
+/* Get reg_class from a letter such as appears in the machine description. */
+
+#define REG_CLASS_FROM_LETTER(LETTER) \
+ ((LETTER) == 'a' ? PREGS : \
+ (LETTER) == 'd' ? DREGS : \
+ (LETTER) == 'z' ? PREGS_CLOBBERED : \
+ (LETTER) == 'D' ? EVEN_DREGS : \
+ (LETTER) == 'W' ? ODD_DREGS : \
+ (LETTER) == 'e' ? AREGS : \
+ (LETTER) == 'A' ? EVEN_AREGS : \
+ (LETTER) == 'B' ? ODD_AREGS : \
+ (LETTER) == 'b' ? IREGS : \
+ (LETTER) == 'B' ? BREGS : \
+ (LETTER) == 'f' ? MREGS : \
+ (LETTER) == 'c' ? CIRCREGS : \
+ (LETTER) == 'C' ? CCREGS : \
+ (LETTER) == 'x' ? MOST_REGS : \
+ (LETTER) == 'y' ? PROLOGUE_REGS : \
+ (LETTER) == 'w' ? NON_A_CC_REGS : \
+ NO_REGS)
+
+/* The same information, inverted:
+ Return the class number of the smallest class containing
+ reg number REGNO. This could be a conditional expression
+ or could index an array. */
+
+#define REGNO_REG_CLASS(REGNO) \
+ ((REGNO) < REG_P0 ? DREGS \
+ : (REGNO) < REG_I0 ? PREGS \
+ : (REGNO) == REG_ARGP ? BASE_REG_CLASS \
+ : (REGNO) >= REG_I0 && (REGNO) <= REG_I3 ? IREGS \
+ : (REGNO) >= REG_L0 && (REGNO) <= REG_L3 ? LREGS \
+ : (REGNO) >= REG_B0 && (REGNO) <= REG_B3 ? BREGS \
+ : (REGNO) >= REG_M0 && (REGNO) <= REG_M3 ? MREGS \
+ : (REGNO) == REG_A0 || (REGNO) == REG_A1 ? AREGS \
+ : (REGNO) == REG_CC ? CCREGS \
+ : (REGNO) >= REG_RETS ? PROLOGUE_REGS \
+ : NO_REGS)
+
+/* When defined, the compiler allows registers explicitly used in the
+ rtl to be used as spill registers but prevents the compiler from
+ extending the lifetime of these registers. */
+#define SMALL_REGISTER_CLASSES 1
+
+#define CLASS_LIKELY_SPILLED_P(CLASS) \
+ ((CLASS) == PREGS_CLOBBERED \
+ || (CLASS) == PROLOGUE_REGS \
+ || (CLASS) == CCREGS)
+
+/* Do not allow to store a value in REG_CC for any mode */
+/* Do not allow to store value in pregs if mode is not SI*/
+#define HARD_REGNO_MODE_OK(REGNO, MODE) hard_regno_mode_ok((REGNO), (MODE))
+
+/* Return the maximum number of consecutive registers
+ needed to represent mode MODE in a register of class CLASS. */
+#define CLASS_MAX_NREGS(CLASS, MODE) \
+ ((GET_MODE_SIZE (MODE) + UNITS_PER_WORD - 1) / UNITS_PER_WORD)
+
+#define HARD_REGNO_NREGS(REGNO, MODE) \
+((MODE) == PDImode && ((REGNO) == REG_A0 || (REGNO) == REG_A1) \
+ ? 1 : CLASS_MAX_NREGS (GENERAL_REGS, MODE))
+
+/* A C expression that is nonzero if hard register TO can be
+ considered for use as a rename register for FROM register */
+#define HARD_REGNO_RENAME_OK(FROM, TO) bfin_hard_regno_rename_ok (FROM, TO)
+
+/* A C expression that is nonzero if it is desirable to choose
+ register allocation so as to avoid move instructions between a
+ value of mode MODE1 and a value of mode MODE2.
+
+ If `HARD_REGNO_MODE_OK (R, MODE1)' and `HARD_REGNO_MODE_OK (R,
+ MODE2)' are ever different for any R, then `MODES_TIEABLE_P (MODE1,
+ MODE2)' must be zero. */
+#define MODES_TIEABLE_P(MODE1, MODE2) ((MODE1) == (MODE2))
+
+/* `PREFERRED_RELOAD_CLASS (X, CLASS)'
+ A C expression that places additional restrictions on the register
+ class to use when it is necessary to copy value X into a register
+ in class CLASS. The value is a register class; perhaps CLASS, or
+ perhaps another, smaller class. */
+#define PREFERRED_RELOAD_CLASS(X, CLASS) (CLASS)
+
+#define SECONDARY_OUTPUT_RELOAD_CLASS(class,mode,x) \
+ secondary_output_reload_class(class,mode,x)
+#define SECONDARY_INPUT_RELOAD_CLASS(class,mode,x) \
+ secondary_input_reload_class(class,mode,x)
+
+/* Function Calling Conventions. */
+
+/* The type of the current function; normal functions are of type
+ SUBROUTINE. */
+typedef enum {
+ SUBROUTINE, INTERRUPT_HANDLER, EXCPT_HANDLER, NMI_HANDLER
+} e_funkind;
+
+#define FUNCTION_ARG_REGISTERS { REG_R0, REG_R1, REG_R2, -1 }
+
+typedef struct {
+ int words; /* # words passed so far */
+ int nregs; /* # registers available for passing */
+ int *arg_regs; /* array of register -1 terminated */
+} CUMULATIVE_ARGS;
+
+/* Define where to put the arguments to a function.
+ Value is zero to push the argument on the stack,
+ or a hard register in which to store the argument.
+
+ MODE is the argument's machine mode.
+ TYPE is the data type of the argument (as a tree).
+ This is null for libcalls where that information may
+ not be available.
+ CUM is a variable of type CUMULATIVE_ARGS which gives info about
+ the preceding args and about the function being called.
+ NAMED is nonzero if this argument is a named parameter
+ (otherwise it is an extra parameter matching an ellipsis). */
+
+#define FUNCTION_ARG(CUM, MODE, TYPE, NAMED) \
+ (function_arg (&CUM, MODE, TYPE, NAMED))
+
+#define FUNCTION_ARG_REGNO_P(REGNO) function_arg_regno_p (REGNO)
+
+
+/* Initialize a variable CUM of type CUMULATIVE_ARGS
+ for a call to a function whose data type is FNTYPE.
+ For a library call, FNTYPE is 0. */
+#define INIT_CUMULATIVE_ARGS(CUM,FNTYPE,LIBNAME,INDIRECT, N_NAMED_ARGS) \
+ (init_cumulative_args (&CUM, FNTYPE, LIBNAME))
+
+/* Update the data in CUM to advance over an argument
+ of mode MODE and data type TYPE.
+ (TYPE is null for libcalls where that information may not be available.) */
+#define FUNCTION_ARG_ADVANCE(CUM, MODE, TYPE, NAMED) \
+ (function_arg_advance (&CUM, MODE, TYPE, NAMED))
+
+#define RETURN_POPS_ARGS(FDECL, FUNTYPE, STKSIZE) 0
+
+/* Define how to find the value returned by a function.
+ VALTYPE is the data type of the value (as a tree).
+ If the precise function being called is known, FUNC is its FUNCTION_DECL;
+ otherwise, FUNC is 0.
+*/
+
+#define VALUE_REGNO(MODE) (REG_R0)
+
+#define FUNCTION_VALUE(VALTYPE, FUNC) \
+ gen_rtx_REG (TYPE_MODE (VALTYPE), \
+ VALUE_REGNO(TYPE_MODE(VALTYPE)))
+
+/* Define how to find the value returned by a library function
+ assuming the value has mode MODE. */
+
+#define LIBCALL_VALUE(MODE) gen_rtx_REG (MODE, VALUE_REGNO(MODE))
+
+#define FUNCTION_VALUE_REGNO_P(N) ((N) == REG_R0)
+
+#define DEFAULT_PCC_STRUCT_RETURN 0
+#define RETURN_IN_MEMORY(TYPE) bfin_return_in_memory(TYPE)
+
+/* Before the prologue, the return address is in the RETS register. */
+#define INCOMING_RETURN_ADDR_RTX gen_rtx_REG (Pmode, REG_RETS)
+
+#define RETURN_ADDR_RTX(COUNT, FRAME) bfin_return_addr_rtx (COUNT)
+
+#define DWARF_FRAME_RETURN_COLUMN DWARF_FRAME_REGNUM (REG_RETS)
+
+/* Call instructions don't modify the stack pointer on the Blackfin. */
+#define INCOMING_FRAME_SP_OFFSET 0
+
+/* Describe how we implement __builtin_eh_return. */
+#define EH_RETURN_DATA_REGNO(N) ((N) < 2 ? (N) : INVALID_REGNUM)
+#define EH_RETURN_STACKADJ_RTX gen_rtx_REG (Pmode, REG_P2)
+#define EH_RETURN_HANDLER_RTX \
+ gen_rtx_MEM (Pmode, plus_constant (frame_pointer_rtx, UNITS_PER_WORD))
+
+/* Addressing Modes */
+
+/* Recognize any constant value that is a valid address. */
+#define CONSTANT_ADDRESS_P(X) (CONSTANT_P (X))
+
+/* Nonzero if the constant value X is a legitimate general operand.
+ symbol_ref are not legitimate and will be put into constant pool.
+ See force_const_mem().
+ If -mno-pool, all constants are legitimate.
+ */
+#define LEGITIMATE_CONSTANT_P(x) 1
+
+/* A number, the maximum number of registers that can appear in a
+ valid memory address. Note that it is up to you to specify a
+ value equal to the maximum number that `GO_IF_LEGITIMATE_ADDRESS'
+ would ever accept. */
+#define MAX_REGS_PER_ADDRESS 1
+
+/* GO_IF_LEGITIMATE_ADDRESS recognizes an RTL expression
+ that is a valid memory address for an instruction.
+ The MODE argument is the machine mode for the MEM expression
+ that wants to use this address.
+
+ Blackfin addressing modes are as follows:
+
+ [preg]
+ [preg + imm16]
+
+ B [ Preg + uimm15 ]
+ W [ Preg + uimm16m2 ]
+ [ Preg + uimm17m4 ]
+
+ [preg++]
+ [preg--]
+ [--sp]
+*/
+
+#define LEGITIMATE_MODE_FOR_AUTOINC_P(MODE) \
+ (GET_MODE_SIZE (MODE) <= 4 || (MODE) == PDImode)
+
+#ifdef REG_OK_STRICT
+#define GO_IF_LEGITIMATE_ADDRESS(MODE, X, WIN) \
+ do { \
+ if (bfin_legitimate_address_p (MODE, X, 1)) \
+ goto WIN; \
+ } while (0);
+#else
+#define GO_IF_LEGITIMATE_ADDRESS(MODE, X, WIN) \
+ do { \
+ if (bfin_legitimate_address_p (MODE, X, 0)) \
+ goto WIN; \
+ } while (0);
+#endif
+
+/* Try machine-dependent ways of modifying an illegitimate address
+ to be legitimate. If we find one, return the new, valid address.
+ This macro is used in only one place: `memory_address' in explow.c.
+
+ OLDX is the address as it was before break_out_memory_refs was called.
+ In some cases it is useful to look at this to decide what needs to be done.
+
+ MODE and WIN are passed so that this macro can use
+ GO_IF_LEGITIMATE_ADDRESS.
+
+ It is always safe for this macro to do nothing. It exists to recognize
+ opportunities to optimize the output.
+ */
+#define LEGITIMIZE_ADDRESS(X,OLDX,MODE,WIN) \
+do { \
+ rtx _q = legitimize_address(X, OLDX, MODE); \
+ if (_q) { X = _q; goto WIN; } \
+} while (0)
+
+#define HAVE_POST_INCREMENT 1
+#define HAVE_POST_DECREMENT 1
+#define HAVE_PRE_DECREMENT 1
+
+/* `LEGITIMATE_PIC_OPERAND_P (X)'
+ A C expression that is nonzero if X is a legitimate immediate
+ operand on the target machine when generating position independent
+ code. You can assume that X satisfies `CONSTANT_P', so you need
+ not check this. You can also assume FLAG_PIC is true, so you need
+ not check it either. You need not define this macro if all
+ constants (including `SYMBOL_REF') can be immediate operands when
+ generating position independent code. */
+#define LEGITIMATE_PIC_OPERAND_P(X) ! SYMBOLIC_CONST (X)
+
+#define SYMBOLIC_CONST(X) \
+(GET_CODE (X) == SYMBOL_REF \
+ || GET_CODE (X) == LABEL_REF \
+ || (GET_CODE (X) == CONST && symbolic_reference_mentioned_p (X)))
+
+/*
+ A C statement or compound statement with a conditional `goto
+ LABEL;' executed if memory address X (an RTX) can have different
+ meanings depending on the machine mode of the memory reference it
+ is used for or if the address is valid for some modes but not
+ others.
+
+ Autoincrement and autodecrement addresses typically have
+ mode-dependent effects because the amount of the increment or
+ decrement is the size of the operand being addressed. Some
+ machines have other mode-dependent addresses. Many RISC machines
+ have no mode-dependent addresses.
+
+ You may assume that ADDR is a valid address for the machine.
+*/
+#define GO_IF_MODE_DEPENDENT_ADDRESS(ADDR,LABEL) \
+do { \
+ if (GET_CODE (ADDR) == POST_INC \
+ || GET_CODE (ADDR) == POST_DEC \
+ || GET_CODE (ADDR) == PRE_DEC) \
+ goto LABEL; \
+} while (0)
+
+#define NOTICE_UPDATE_CC(EXPR, INSN) 0
+
+/* Value is 1 if truncating an integer of INPREC bits to OUTPREC bits
+ is done just by pretending it is already truncated. */
+#define TRULY_NOOP_TRUNCATION(OUTPREC, INPREC) 1
+
+/* Max number of bytes we can move from memory to memory
+ in one reasonably fast instruction. */
+#define MOVE_MAX UNITS_PER_WORD
+
+
+/* STORAGE LAYOUT: target machine storage layout
+ Define this macro as a C expression which is nonzero if accessing
+ less than a word of memory (i.e. a `char' or a `short') is no
+ faster than accessing a word of memory, i.e., if such access
+ require more than one instruction or if there is no difference in
+ cost between byte and (aligned) word loads.
+
+ When this macro is not defined, the compiler will access a field by
+ finding the smallest containing object; when it is defined, a
+ fullword load will be used if alignment permits. Unless bytes
+ accesses are faster than word accesses, using word accesses is
+ preferable since it may eliminate subsequent memory access if
+ subsequent accesses occur to other fields in the same word of the
+ structure, but to different bytes. */
+#define SLOW_BYTE_ACCESS 0
+#define SLOW_SHORT_ACCESS 0
+
+/* Define this if most significant bit is lowest numbered
+ in instructions that operate on numbered bit-fields. */
+#define BITS_BIG_ENDIAN 0
+
+/* Define this if most significant byte of a word is the lowest numbered.
+ We can't access bytes but if we could we would in the Big Endian order. */
+#define BYTES_BIG_ENDIAN 0
+
+/* Define this if most significant word of a multiword number is numbered. */
+#define WORDS_BIG_ENDIAN 0
+
+/* number of bits in an addressable storage unit */
+#define BITS_PER_UNIT 8
+
+/* Width in bits of a "word", which is the contents of a machine register.
+ Note that this is not necessarily the width of data type `int';
+ if using 16-bit ints on a 68000, this would still be 32.
+ But on a machine with 16-bit registers, this would be 16. */
+#define BITS_PER_WORD 32
+
+/* Width of a word, in units (bytes). */
+#define UNITS_PER_WORD 4
+
+/* Size of a vector for autovectorization. */
+#define UNITS_PER_SIMD_WORD 4
+
+/* Width in bits of a pointer.
+ See also the macro `Pmode1' defined below. */
+#define POINTER_SIZE 32
+
+/* Allocation boundary (in *bits*) for storing pointers in memory. */
+#define POINTER_BOUNDARY 32
+
+/* Allocation boundary (in *bits*) for storing arguments in argument list. */
+#define PARM_BOUNDARY 32
+
+/* Boundary (in *bits*) on which stack pointer should be aligned. */
+#define STACK_BOUNDARY 32
+
+/* Allocation boundary (in *bits*) for the code of a function. */
+#define FUNCTION_BOUNDARY 32
+
+/* Alignment of field after `int : 0' in a structure. */
+#define EMPTY_FIELD_BOUNDARY BITS_PER_WORD
+
+/* No data type wants to be aligned rounder than this. */
+#define BIGGEST_ALIGNMENT 32
+
+/* Define this if move instructions will actually fail to work
+ when given unaligned data. */
+#define STRICT_ALIGNMENT 1
+
+/* (shell-command "rm c-decl.o stor-layout.o")
+ * never define PCC_BITFIELD_TYPE_MATTERS
+ * really cause some alignment problem
+ */
+
+#define UNITS_PER_FLOAT ((FLOAT_TYPE_SIZE + BITS_PER_UNIT - 1) / \
+ BITS_PER_UNIT)
+
+#define UNITS_PER_DOUBLE ((DOUBLE_TYPE_SIZE + BITS_PER_UNIT - 1) / \
+ BITS_PER_UNIT)
+
+
+/* what is the 'type' of size_t */
+#define SIZE_TYPE "long unsigned int"
+
+/* Define this as 1 if `char' should by default be signed; else as 0. */
+#define DEFAULT_SIGNED_CHAR 1
+#define FLOAT_TYPE_SIZE BITS_PER_WORD
+#define SHORT_TYPE_SIZE 16
+#define CHAR_TYPE_SIZE 8
+#define INT_TYPE_SIZE 32
+#define LONG_TYPE_SIZE 32
+#define LONG_LONG_TYPE_SIZE 64
+
+/* Note: Fix this to depend on target switch. -- lev */
+
+/* Note: Try to implement double and force long double. -- tonyko
+ * #define __DOUBLES_ARE_FLOATS__
+ * #define DOUBLE_TYPE_SIZE FLOAT_TYPE_SIZE
+ * #define LONG_DOUBLE_TYPE_SIZE DOUBLE_TYPE_SIZE
+ * #define DOUBLES_ARE_FLOATS 1
+ */
+
+#define DOUBLE_TYPE_SIZE 64
+#define LONG_DOUBLE_TYPE_SIZE 64
+
+/* `PROMOTE_MODE (M, UNSIGNEDP, TYPE)'
+ A macro to update M and UNSIGNEDP when an object whose type is
+ TYPE and which has the specified mode and signedness is to be
+ stored in a register. This macro is only called when TYPE is a
+ scalar type.
+
+ On most RISC machines, which only have operations that operate on
+ a full register, define this macro to set M to `word_mode' if M is
+ an integer mode narrower than `BITS_PER_WORD'. In most cases,
+ only integer modes should be widened because wider-precision
+ floating-point operations are usually more expensive than their
+ narrower counterparts.
+
+ For most machines, the macro definition does not change UNSIGNEDP.
+ However, some machines, have instructions that preferentially
+ handle either signed or unsigned quantities of certain modes. For
+ example, on the DEC Alpha, 32-bit loads from memory and 32-bit add
+ instructions sign-extend the result to 64 bits. On such machines,
+ set UNSIGNEDP according to which kind of extension is more
+ efficient.
+
+ Do not define this macro if it would never modify M.*/
+
+#define BFIN_PROMOTE_MODE_P(MODE) \
+ (!TARGET_DSP && GET_MODE_CLASS (MODE) == MODE_INT \
+ && GET_MODE_SIZE (MODE) < UNITS_PER_WORD)
+
+#define PROMOTE_MODE(MODE, UNSIGNEDP, TYPE) \
+ if (BFIN_PROMOTE_MODE_P(MODE)) \
+ { \
+ if (MODE == QImode) \
+ UNSIGNEDP = 1; \
+ else if (MODE == HImode) \
+ UNSIGNEDP = 0; \
+ (MODE) = SImode; \
+ }
+
+/* Describing Relative Costs of Operations */
+
+/* Do not put function addr into constant pool */
+#define NO_FUNCTION_CSE 1
+
+/* A C expression for the cost of moving data from a register in class FROM to
+ one in class TO. The classes are expressed using the enumeration values
+ such as `GENERAL_REGS'. A value of 2 is the default; other values are
+ interpreted relative to that.
+
+ It is not required that the cost always equal 2 when FROM is the same as TO;
+ on some machines it is expensive to move between registers if they are not
+ general registers. */
+
+#define REGISTER_MOVE_COST(MODE, CLASS1, CLASS2) \
+ bfin_register_move_cost ((MODE), (CLASS1), (CLASS2))
+
+/* A C expression for the cost of moving data of mode M between a
+ register and memory. A value of 2 is the default; this cost is
+ relative to those in `REGISTER_MOVE_COST'.
+
+ If moving between registers and memory is more expensive than
+ between two registers, you should define this macro to express the
+ relative cost. */
+
+#define MEMORY_MOVE_COST(MODE, CLASS, IN) \
+ bfin_memory_move_cost ((MODE), (CLASS), (IN))
+
+/* Specify the machine mode that this machine uses
+ for the index in the tablejump instruction. */
+#define CASE_VECTOR_MODE SImode
+
+#define JUMP_TABLES_IN_TEXT_SECTION flag_pic
+
+/* Define if operations between registers always perform the operation
+ on the full register even if a narrower mode is specified.
+#define WORD_REGISTER_OPERATIONS
+*/
+
+#define CONST_18UBIT_IMM_P(VALUE) ((VALUE) >= 0 && (VALUE) <= 262140)
+#define CONST_16BIT_IMM_P(VALUE) ((VALUE) >= -32768 && (VALUE) <= 32767)
+#define CONST_16UBIT_IMM_P(VALUE) ((VALUE) >= 0 && (VALUE) <= 65535)
+#define CONST_7BIT_IMM_P(VALUE) ((VALUE) >= -64 && (VALUE) <= 63)
+#define CONST_7NBIT_IMM_P(VALUE) ((VALUE) >= -64 && (VALUE) <= 0)
+#define CONST_5UBIT_IMM_P(VALUE) ((VALUE) >= 0 && (VALUE) <= 31)
+#define CONST_4BIT_IMM_P(VALUE) ((VALUE) >= -8 && (VALUE) <= 7)
+#define CONST_4UBIT_IMM_P(VALUE) ((VALUE) >= 0 && (VALUE) <= 15)
+#define CONST_3BIT_IMM_P(VALUE) ((VALUE) >= -4 && (VALUE) <= 3)
+#define CONST_3UBIT_IMM_P(VALUE) ((VALUE) >= 0 && (VALUE) <= 7)
+
+#define CONSTRAINT_LEN(C, STR) \
+ ((C) == 'P' || (C) == 'M' || (C) == 'N' ? 2 \
+ : (C) == 'K' ? 3 \
+ : DEFAULT_CONSTRAINT_LEN ((C), (STR)))
+
+#define CONST_OK_FOR_P(VALUE, STR) \
+ ((STR)[1] == '0' ? (VALUE) == 0 \
+ : (STR)[1] == '1' ? (VALUE) == 1 \
+ : (STR)[1] == '2' ? (VALUE) == 2 \
+ : (STR)[1] == '3' ? (VALUE) == 3 \
+ : (STR)[1] == '4' ? (VALUE) == 4 \
+ : 0)
+
+#define CONST_OK_FOR_K(VALUE, STR) \
+ ((STR)[1] == 'u' \
+ ? ((STR)[2] == '3' ? CONST_3UBIT_IMM_P (VALUE) \
+ : (STR)[2] == '4' ? CONST_4UBIT_IMM_P (VALUE) \
+ : (STR)[2] == '5' ? CONST_5UBIT_IMM_P (VALUE) \
+ : (STR)[2] == 'h' ? CONST_16UBIT_IMM_P (VALUE) \
+ : 0) \
+ : (STR)[1] == 's' \
+ ? ((STR)[2] == '3' ? CONST_3BIT_IMM_P (VALUE) \
+ : (STR)[2] == '4' ? CONST_4BIT_IMM_P (VALUE) \
+ : (STR)[2] == '7' ? CONST_7BIT_IMM_P (VALUE) \
+ : (STR)[2] == 'h' ? CONST_16BIT_IMM_P (VALUE) \
+ : 0) \
+ : (STR)[1] == 'n' \
+ ? ((STR)[2] == '7' ? CONST_7NBIT_IMM_P (VALUE) \
+ : 0) \
+ : 0)
+
+#define CONST_OK_FOR_M(VALUE, STR) \
+ ((STR)[1] == '1' ? (VALUE) == 255 \
+ : (STR)[1] == '2' ? (VALUE) == 65535 \
+ : 0)
+
+/* The letters I, J, K, L and M in a register constraint string
+ can be used to stand for particular ranges of immediate operands.
+ This macro defines what the ranges are.
+ C is the letter, and VALUE is a constant value.
+ Return 1 if VALUE is in the range specified by C.
+
+ bfin constant operands are as follows
+
+ J 2**N 5bit imm scaled
+ Ks7 -64 .. 63 signed 7bit imm
+ Ku5 0..31 unsigned 5bit imm
+ Ks4 -8 .. 7 signed 4bit imm
+ Ks3 -4 .. 3 signed 3bit imm
+ Ku3 0 .. 7 unsigned 3bit imm
+ Pn 0, 1, 2 constants 0, 1 or 2, corresponding to n
+*/
+#define CONST_OK_FOR_CONSTRAINT_P(VALUE, C, STR) \
+ ((C) == 'J' ? (log2constp (VALUE)) \
+ : (C) == 'K' ? CONST_OK_FOR_K (VALUE, STR) \
+ : (C) == 'L' ? log2constp (~(VALUE)) \
+ : (C) == 'M' ? CONST_OK_FOR_M (VALUE, STR) \
+ : (C) == 'P' ? CONST_OK_FOR_P (VALUE, STR) \
+ : 0)
+
+ /*Constant Output Formats */
+#define CONST_DOUBLE_OK_FOR_LETTER_P(VALUE, C) \
+ ((C) == 'H' ? 1 : 0)
+
+#define EXTRA_CONSTRAINT(VALUE, D) \
+ ((D) == 'Q' ? GET_CODE (VALUE) == SYMBOL_REF : 0)
+
+/* `FINALIZE_PIC'
+ By generating position-independent code, when two different
+ programs (A and B) share a common library (libC.a), the text of
+ the library can be shared whether or not the library is linked at
+ the same address for both programs. In some of these
+ environments, position-independent code requires not only the use
+ of different addressing modes, but also special code to enable the
+ use of these addressing modes.
+
+ The `FINALIZE_PIC' macro serves as a hook to emit these special
+ codes once the function is being compiled into assembly code, but
+ not before. (It is not done before, because in the case of
+ compiling an inline function, it would lead to multiple PIC
+ prologues being included in functions which used inline functions
+ and were compiled to assembly language.) */
+#define FINALIZE_PIC do {} while (0)
+
+/* Switch into a generic section. */
+#define TARGET_ASM_NAMED_SECTION default_elf_asm_named_section
+
+#define PRINT_OPERAND(FILE, RTX, CODE) print_operand (FILE, RTX, CODE)
+#define PRINT_OPERAND_ADDRESS(FILE, RTX) print_address_operand (FILE, RTX)
+
+typedef enum sections {
+ CODE_DIR,
+ DATA_DIR,
+ LAST_SECT_NM
+} SECT_ENUM_T;
+
+typedef enum directives {
+ LONG_CONST_DIR,
+ SHORT_CONST_DIR,
+ BYTE_CONST_DIR,
+ SPACE_DIR,
+ INIT_DIR,
+ LAST_DIR_NM
+} DIR_ENUM_T;
+
+#define TEXT_SECTION_ASM_OP ".text;"
+#define DATA_SECTION_ASM_OP ".data;"
+
+#define ASM_APP_ON ""
+#define ASM_APP_OFF ""
+
+#define ASM_GLOBALIZE_LABEL1(FILE, NAME) \
+ do { fputs (".global ", FILE); \
+ assemble_name (FILE, NAME); \
+ fputc (';',FILE); \
+ fputc ('\n',FILE); \
+ } while (0)
+
+#define ASM_DECLARE_FUNCTION_NAME(FILE,NAME,DECL) \
+ do { \
+ fputs (".type ", FILE); \
+ assemble_name (FILE, NAME); \
+ fputs (", STT_FUNC", FILE); \
+ fputc (';',FILE); \
+ fputc ('\n',FILE); \
+ ASM_OUTPUT_LABEL(FILE, NAME); \
+ } while (0)
+
+#define ASM_OUTPUT_LABEL(FILE, NAME) \
+ do { assemble_name (FILE, NAME); \
+ fputs (":\n",FILE); \
+ } while (0)
+
+#define ASM_OUTPUT_LABELREF(FILE,NAME) \
+ do { fprintf (FILE, "_%s", NAME); \
+ } while (0)
+
+#define ASM_FORMAT_PRIVATE_NAME(OUTPUT, NAME, LABELNO) \
+ do { \
+ int len = strlen (NAME); \
+ char *temp = (char *) alloca (len + 4); \
+ temp[0] = 'L'; \
+ temp[1] = '_'; \
+ strcpy (&temp[2], (NAME)); \
+ temp[len + 2] = '_'; \
+ temp[len + 3] = 0; \
+ (OUTPUT) = (char *) alloca (strlen (NAME) + 13); \
+ sprintf (OUTPUT, "_%s$%d", temp, LABELNO); \
+ } while (0)
+
+#define ASM_OUTPUT_ADDR_VEC_ELT(FILE, VALUE) \
+do { char __buf[256]; \
+ fprintf (FILE, "\t.dd\t"); \
+ ASM_GENERATE_INTERNAL_LABEL (__buf, "L", VALUE); \
+ assemble_name (FILE, __buf); \
+ fputc (';', FILE); \
+ fputc ('\n', FILE); \
+ } while (0)
+
+#define ASM_OUTPUT_ADDR_DIFF_ELT(FILE, BODY, VALUE, REL) \
+ MY_ASM_OUTPUT_ADDR_DIFF_ELT(FILE, VALUE, REL)
+
+#define MY_ASM_OUTPUT_ADDR_DIFF_ELT(FILE, VALUE, REL) \
+ do { \
+ char __buf[256]; \
+ fprintf (FILE, "\t.dd\t"); \
+ ASM_GENERATE_INTERNAL_LABEL (__buf, "L", VALUE); \
+ assemble_name (FILE, __buf); \
+ fputs (" - ", FILE); \
+ ASM_GENERATE_INTERNAL_LABEL (__buf, "L", REL); \
+ assemble_name (FILE, __buf); \
+ fputc (';', FILE); \
+ fputc ('\n', FILE); \
+ } while (0)
+
+#define ASM_OUTPUT_ALIGN(FILE,LOG) \
+ do { \
+ fprintf (FILE, ".align %d\n", LOG); \
+ } while (0)
+
+#define ASM_OUTPUT_SKIP(FILE,SIZE) \
+ do { \
+ asm_output_skip (FILE, SIZE); \
+ } while (0)
+
+#define ASM_OUTPUT_LOCAL(FILE, NAME, SIZE, ROUNDED) \
+do { \
+ data_section(); \
+ if ((SIZE) >= (unsigned int) 4 ) ASM_OUTPUT_ALIGN(FILE,2); \
+ ASM_OUTPUT_SIZE_DIRECTIVE (FILE, NAME, SIZE); \
+ ASM_OUTPUT_LABEL (FILE, NAME); \
+ fprintf (FILE, "%s %ld;\n", ASM_SPACE, \
+ (ROUNDED) > (unsigned int) 1 ? (ROUNDED) : 1); \
+} while (0)
+
+#define ASM_OUTPUT_COMMON(FILE, NAME, SIZE, ROUNDED) \
+ do { \
+ ASM_GLOBALIZE_LABEL1(FILE,NAME); \
+ ASM_OUTPUT_LOCAL (FILE, NAME, SIZE, ROUNDED); } while(0)
+
+#define ASM_COMMENT_START "//"
+
+#define FUNCTION_PROFILER(FILE, LABELNO) \
+ do {\
+ fprintf (FILE, "\tP1.l =LP$%d; P1.h =LP$%d; call mcount;\n", \
+ LABELNO, LABELNO);\
+ } while(0)
+
+#define ASM_OUTPUT_REG_PUSH(FILE, REGNO) fprintf (FILE, "[SP--] = %s;\n", reg_names[REGNO])
+#define ASM_OUTPUT_REG_POP(FILE, REGNO) fprintf (FILE, "%s = [SP++];\n", reg_names[REGNO])
+
+extern struct rtx_def *bfin_compare_op0, *bfin_compare_op1;
+extern struct rtx_def *bfin_cc_rtx, *bfin_rets_rtx;
+
+/* This works for GAS and some other assemblers. */
+#define SET_ASM_OP ".set "
+
+/* Don't know how to order these. UNALIGNED_WORD_ASM_OP is in
+ dwarf2.out. */
+#define UNALIGNED_WORD_ASM_OP ".4byte"
+
+/* DBX register number for a given compiler register number */
+#define DBX_REGISTER_NUMBER(REGNO) (REGNO)
+
+#define SIZE_ASM_OP "\t.size\t"
+
+#endif /* _BFIN_CONFIG */
--- /dev/null
+;;- Machine description for Blackfin for GNU compiler
+;; Copyright 2005 Free Software Foundation, Inc.
+;; Contributed by Analog Devices.
+
+;; 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 2, 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 COPYING. If not, write to
+;; the Free Software Foundation, 59 Temple Place - Suite 330,
+;; Boston, MA 02111-1307, USA.
+
+; operand punctuation marks:
+;
+; X -- integer value printed as log2
+; Y -- integer value printed as log2(~value) - for bitclear
+; h -- print half word register, low part
+; d -- print half word register, high part
+; D -- print operand as dregs pairs
+; w -- print operand as accumulator register word (a0w, a1w)
+; H -- high part of double mode operand
+; T -- byte register representation Oct. 02 2001
+
+; constant operand classes
+;
+; J 2**N 5bit imm scaled
+; Ks7 -64 .. 63 signed 7bit imm
+; Ku5 0..31 unsigned 5bit imm
+; Ks4 -8 .. 7 signed 4bit imm
+; Ks3 -4 .. 3 signed 3bit imm
+; Ku3 0 .. 7 unsigned 3bit imm
+; Pn 0, 1, 2 constants 0, 1 or 2, corresponding to n
+;
+; register operands
+; d (r0..r7)
+; a (p0..p5,fp,sp)
+; e (a0, a1)
+; b (i0..i3)
+; f (m0..m3)
+; B
+; c (i0..i3,m0..m3) CIRCREGS
+; C (CC) CCREGS
+;
+
+;; Define constants for hard registers.
+
+(define_constants
+ [(REG_R0 0)
+ (REG_R1 1)
+ (REG_R2 2)
+ (REG_R3 3)
+ (REG_R4 4)
+ (REG_R5 5)
+ (REG_R6 6)
+ (REG_R7 7)
+
+ (REG_P0 8)
+ (REG_P1 9)
+ (REG_P2 10)
+ (REG_P3 11)
+ (REG_P4 12)
+ (REG_P5 13)
+ (REG_P6 14)
+ (REG_P7 15)
+
+ (REG_SP 14)
+ (REG_FP 15)
+
+ (REG_I0 16)
+ (REG_B0 17)
+ (REG_L0 18)
+ (REG_I1 19)
+ (REG_B1 20)
+ (REG_L1 21)
+ (REG_I2 22)
+ (REG_B2 23)
+ (REG_L2 24)
+ (REG_I3 25)
+ (REG_B3 26)
+ (REG_L3 27)
+
+ (REG_M0 28)
+ (REG_M1 29)
+ (REG_M2 30)
+ (REG_M3 31)
+
+ (REG_A0 32)
+ (REG_A1 33)
+
+ (REG_CC 34)
+ (REG_RETS 35)
+ (REG_RETI 36)
+ (REG_RETX 37)
+ (REG_RETN 38)
+ (REG_RETE 39)
+
+ (REG_ASTAT 40)
+ (REG_SEQSTAT 41)
+ (REG_USP 42)
+
+ (REG_ARGP 43)])
+
+;; Constants used in UNSPECs and UNSPEC_VOLATILEs.
+
+(define_constants
+ [(UNSPEC_CBRANCH_TAKEN 0)
+ (UNSPEC_CBRANCH_NOPS 1)
+ (UNSPEC_RETURN 2)
+ (UNSPEC_MOVE_PIC 3)
+ (UNSPEC_LIBRARY_OFFSET 4)
+ (UNSPEC_PUSH_MULTIPLE 5)])
+
+(define_constants
+ [(UNSPEC_VOLATILE_EH_RETURN 0)])
+
+(define_attr "type"
+ "move,mvi,mcld,mcst,dsp32,mult,alu0,shft,brcc,br,call,misc,compare,dummy"
+ (const_string "misc"))
+
+;; Scheduling definitions
+
+(define_automaton "bfin")
+
+(define_cpu_unit "core" "bfin")
+
+(define_insn_reservation "alu" 1
+ (eq_attr "type" "move,mvi,mcst,dsp32,alu0,shft,brcc,br,call,misc,compare")
+ "core")
+
+(define_insn_reservation "imul" 3
+ (eq_attr "type" "mult")
+ "core*3")
+
+(define_insn_reservation "load" 1
+ (eq_attr "type" "mcld")
+ "core")
+
+;; Make sure genautomata knows about the maximum latency that can be produced
+;; by the adjust_cost function.
+(define_insn_reservation "dummy" 5
+ (eq_attr "type" "mcld")
+ "core")
+\f
+;; Operand and operator predicates
+
+(include "predicates.md")
+
+\f
+;;; FRIO branches have been optimized for code density
+;;; this comes at a slight cost of complexity when
+;;; a compiler needs to generate branches in the general
+;;; case. In order to generate the correct branching
+;;; mechanisms the compiler needs keep track of instruction
+;;; lengths. The follow table describes how to count instructions
+;;; for the FRIO architecture.
+;;;
+;;; unconditional br are 12-bit imm pcrelative branches *2
+;;; conditional br are 10-bit imm pcrelative branches *2
+;;; brcc 10-bit:
+;;; 1024 10-bit imm *2 is 2048 (-1024..1022)
+;;; br 12-bit :
+;;; 4096 12-bit imm *2 is 8192 (-4096..4094)
+;;; NOTE : For brcc we generate instructions such as
+;;; if cc jmp; jump.[sl] offset
+;;; offset of jump.[sl] is from the jump instruction but
+;;; gcc calculates length from the if cc jmp instruction
+;;; hence our range is (-4094, 4096) instead of (-4096, 4094) for a br
+;;;
+;;; The way the (pc) rtx works in these calculations is somewhat odd;
+;;; for backward branches it's the address of the current instruction,
+;;; for forward branches it's the previously known address of the following
+;;; instruction - we have to take this into account by reducing the range
+;;; for a forward branch.
+
+;; Lengths for type "mvi" insns are always defined by the instructions
+;; themselves.
+(define_attr "length" ""
+ (cond [(eq_attr "type" "mcld")
+ (if_then_else (match_operand 1 "effective_address_32bit_p" "")
+ (const_int 4) (const_int 2))
+
+ (eq_attr "type" "mcst")
+ (if_then_else (match_operand 0 "effective_address_32bit_p" "")
+ (const_int 4) (const_int 2))
+
+ (eq_attr "type" "move") (const_int 2)
+
+ (eq_attr "type" "dsp32") (const_int 4)
+ (eq_attr "type" "call") (const_int 4)
+
+ (eq_attr "type" "br")
+ (if_then_else (and
+ (le (minus (match_dup 0) (pc)) (const_int 4092))
+ (ge (minus (match_dup 0) (pc)) (const_int -4096)))
+ (const_int 2)
+ (const_int 4))
+
+ (eq_attr "type" "brcc")
+ (cond [(and
+ (le (minus (match_dup 3) (pc)) (const_int 1020))
+ (ge (minus (match_dup 3) (pc)) (const_int -1024)))
+ (const_int 2)
+ (and
+ (le (minus (match_dup 3) (pc)) (const_int 4096))
+ (ge (minus (match_dup 3) (pc)) (const_int -4094)))
+ (const_int 4)]
+ (const_int 6))
+ ]
+
+ (const_int 2)))
+
+;; Conditional moves
+
+(define_expand "movsicc"
+ [(set (match_operand:SI 0 "register_operand" "")
+ (if_then_else:SI (match_operand 1 "comparison_operator" "")
+ (match_operand:SI 2 "register_operand" "")
+ (match_operand:SI 3 "register_operand" "")))]
+ ""
+{
+ operands[1] = bfin_gen_compare (operands[1], SImode);
+})
+
+(define_insn "*movsicc_insn1"
+ [(set (match_operand:SI 0 "register_operand" "=da,da,da")
+ (if_then_else:SI
+ (eq:BI (match_operand:BI 3 "cc_operand" "C,C,C")
+ (const_int 0))
+ (match_operand:SI 1 "register_operand" "da,0,da")
+ (match_operand:SI 2 "register_operand" "0,da,da")))]
+ ""
+ "@
+ if !cc %0 =%1; /* movsicc-1a */
+ if cc %0 =%2; /* movsicc-1b */
+ if !cc %0 =%1; if cc %0=%2; /* movsicc-1 */"
+ [(set_attr "length" "2,2,4")
+ (set_attr "type" "move")])
+
+(define_insn "*movsicc_insn2"
+ [(set (match_operand:SI 0 "register_operand" "=da,da,da")
+ (if_then_else:SI
+ (ne:BI (match_operand:BI 3 "cc_operand" "C,C,C")
+ (const_int 0))
+ (match_operand:SI 1 "register_operand" "0,da,da")
+ (match_operand:SI 2 "register_operand" "da,0,da")))]
+ ""
+ "@
+ if !cc %0 =%2; /* movsicc-2b */
+ if cc %0 =%1; /* movsicc-2a */
+ if cc %0 =%1; if !cc %0=%2; /* movsicc-1 */"
+ [(set_attr "length" "2,2,4")
+ (set_attr "type" "move")])
+
+;; Insns to load HIGH and LO_SUM
+
+(define_insn "movsi_high"
+ [(set (match_operand:SI 0 "register_operand" "=x")
+ (high:SI (match_operand:SI 1 "immediate_operand" "i")))]
+ "reload_completed"
+ "%d0 = %d1;"
+ [(set_attr "type" "mvi")
+ (set_attr "length" "4")])
+
+(define_insn "movstricthi_high"
+ [(set (match_operand:SI 0 "register_operand" "+x")
+ (ior:SI (and:SI (match_dup 0) (const_int 65535))
+ (match_operand:SI 1 "immediate_operand" "i")))]
+ "reload_completed"
+ "%d0 = %d1;"
+ [(set_attr "type" "mvi")
+ (set_attr "length" "4")])
+
+(define_insn "movsi_low"
+ [(set (match_operand:SI 0 "register_operand" "=x")
+ (lo_sum:SI (match_operand:SI 1 "register_operand" "0")
+ (match_operand:SI 2 "immediate_operand" "i")))]
+ "reload_completed"
+ "%h0 = %h2;"
+ [(set_attr "type" "mvi")
+ (set_attr "length" "4")])
+
+(define_insn "movsi_high_pic"
+ [(set (match_operand:SI 0 "register_operand" "=x")
+ (high:SI (unspec:SI [(match_operand:SI 1 "" "")]
+ UNSPEC_MOVE_PIC)))]
+ ""
+ "%d0 = %1@GOT_LOW;"
+ [(set_attr "type" "mvi")
+ (set_attr "length" "4")])
+
+(define_insn "movsi_low_pic"
+ [(set (match_operand:SI 0 "register_operand" "=x")
+ (lo_sum:SI (match_operand:SI 1 "register_operand" "0")
+ (unspec:SI [(match_operand:SI 2 "" "")]
+ UNSPEC_MOVE_PIC)))]
+ ""
+ "%h0 = %h2@GOT_HIGH;"
+ [(set_attr "type" "mvi")
+ (set_attr "length" "4")])
+
+;;; Move instructions
+
+(define_insn_and_split "movdi_insn"
+ [(set (match_operand:DI 0 "nonimmediate_operand" "=x,mx,r")
+ (match_operand:DI 1 "general_operand" "iFx,r,mx"))]
+ "GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM"
+ "#"
+ "reload_completed"
+ [(set (match_dup 2) (match_dup 3))
+ (set (match_dup 4) (match_dup 5))]
+{
+ rtx lo_half[2], hi_half[2];
+ split_di (operands, 2, lo_half, hi_half);
+
+ if (reg_overlap_mentioned_p (lo_half[0], hi_half[1]))
+ {
+ operands[2] = hi_half[0];
+ operands[3] = hi_half[1];
+ operands[4] = lo_half[0];
+ operands[5] = lo_half[1];
+ }
+ else
+ {
+ operands[2] = lo_half[0];
+ operands[3] = lo_half[1];
+ operands[4] = hi_half[0];
+ operands[5] = hi_half[1];
+ }
+})
+
+(define_insn "movbi"
+ [(set (match_operand:BI 0 "nonimmediate_operand" "=x,x,d,mr,C,d")
+ (match_operand:BI 1 "general_operand" "x,xKs3,mr,d,d,C"))]
+
+ ""
+ "@
+ %0 = %1;
+ %0 = %1 (X);
+ %0 = %1;
+ %0 = %1;
+ CC = %1;
+ %0 = CC;"
+ [(set_attr "type" "move,mvi,mcld,mcst,compare,compare")
+ (set_attr "length" "2,2,*,*,2,2")])
+
+(define_insn "movpdi"
+ [(set (match_operand:PDI 0 "nonimmediate_operand" "=e,<,e")
+ (match_operand:PDI 1 "general_operand" " e,e,>"))]
+ ""
+ "@
+ %0 = %1;
+ %0 = %x1; %0 = %w1;
+ %w0 = %1; %x0 = %1;"
+ [(set_attr "type" "move,mcst,mcld")])
+
+(define_insn "*pushsi_insn"
+ [(set (mem:SI (pre_dec:SI (reg:SI REG_SP)))
+ (match_operand:SI 0 "register_operand" "xy"))]
+ ""
+ "[--SP] = %0;"
+ [(set_attr "type" "mcst")
+ (set_attr "length" "2")])
+
+(define_insn "*popsi_insn"
+ [(set (match_operand:SI 0 "register_operand" "=xy")
+ (mem:SI (post_inc:SI (reg:SI REG_SP))))]
+ ""
+ "%0 = [SP++];"
+ [(set_attr "type" "mcld")
+ (set_attr "length" "2")])
+
+;; The first alternative is used to make reload choose a limited register
+;; class when faced with a movsi_insn that had its input operand replaced
+;; with a PLUS. We generally require fewer secondary reloads this way.
+(define_insn "*movsi_insn"
+ [(set (match_operand:SI 0 "nonimmediate_operand" "=da,x*y,da,x,x,x,da,mr")
+ (match_operand:SI 1 "general_operand" "da,x*y,xKs7,xKsh,xKuh,ix,mr,da"))]
+
+ "GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM"
+ "@
+ %0 = %1;
+ %0 = %1;
+ %0 = %1 (X);
+ %0 = %1 (X);
+ %0 = %1 (Z);
+ #
+ %0 = %1;
+ %0 = %1;"
+ [(set_attr "type" "move,move,mvi,mvi,mvi,*,mcld,mcst")
+ (set_attr "length" "2,2,2,4,4,*,*,*")])
+
+(define_insn "*movv2hi_insn"
+ [(set (match_operand:V2HI 0 "nonimmediate_operand" "=da,d,m")
+ (match_operand:V2HI 1 "general_operand" "d,m,d"))]
+
+ "GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM"
+ "%0 = %1;"
+ [(set_attr "type" "move,mcld,mcst")
+ (set_attr "length" "2,*,*")])
+
+(define_insn "*movhi_insn"
+ [(set (match_operand:HI 0 "nonimmediate_operand" "=x,da,x,d,mr")
+ (match_operand:HI 1 "general_operand" "x,xKs7,xKsh,mr,d"))]
+ "GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM"
+ "@
+ %0 = %1;
+ %0 = %1 (X);
+ %0 = %1 (X);
+ %0 = W %1 (X);
+ W %0 = %1;"
+ [(set_attr "type" "move,mvi,mvi,mcld,mcst")
+ (set_attr "length" "2,2,4,*,*")])
+
+(define_insn "*movqi_insn"
+ [(set (match_operand:QI 0 "nonimmediate_operand" "=x,da,x,d,mr")
+ (match_operand:QI 1 "general_operand" "x,xKs7,xKsh,mr,d"))]
+ "GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM"
+ "@
+ %0 = %1;
+ %0 = %1 (X);
+ %0 = %1 (X);
+ %0 = B %1 (X);
+ B %0 = %1;"
+ [(set_attr "type" "move,mvi,mvi,mcld,mcst")
+ (set_attr "length" "2,2,4,*,*")])
+
+(define_insn "*movsf_insn"
+ [(set (match_operand:SF 0 "nonimmediate_operand" "=x,x,da,mr")
+ (match_operand:SF 1 "general_operand" "x,Fx,mr,da"))]
+ "GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM"
+ "@
+ %0 = %1;
+ #
+ %0 = %1;
+ %0 = %1;"
+ [(set_attr "type" "move,*,mcld,mcst")])
+
+(define_insn_and_split "movdf_insn"
+ [(set (match_operand:DF 0 "nonimmediate_operand" "=x,mx,r")
+ (match_operand:DF 1 "general_operand" "iFx,r,mx"))]
+ "GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM"
+ "#"
+ "reload_completed"
+ [(set (match_dup 2) (match_dup 3))
+ (set (match_dup 4) (match_dup 5))]
+{
+ rtx lo_half[2], hi_half[2];
+ split_di (operands, 2, lo_half, hi_half);
+
+ if (reg_overlap_mentioned_p (lo_half[0], hi_half[1]))
+ {
+ operands[2] = hi_half[0];
+ operands[3] = hi_half[1];
+ operands[4] = lo_half[0];
+ operands[5] = lo_half[1];
+ }
+ else
+ {
+ operands[2] = lo_half[0];
+ operands[3] = lo_half[1];
+ operands[4] = hi_half[0];
+ operands[5] = hi_half[1];
+ }
+})
+
+;; This is the main "hook" for PIC code. When generating
+;; PIC, movsi is responsible for determining when the source address
+;; needs PIC relocation and appropriately calling legitimize_pic_address
+;; to perform the actual relocation.
+
+(define_expand "movsi"
+ [(set (match_operand:SI 0 "nonimmediate_operand" "")
+ (match_operand:SI 1 "general_operand" ""))]
+ ""
+ "expand_move (operands, SImode);")
+
+(define_expand "movv2hi"
+ [(set (match_operand:V2HI 0 "nonimmediate_operand" "")
+ (match_operand:V2HI 1 "general_operand" ""))]
+ ""
+ "expand_move (operands, V2HImode);")
+
+(define_expand "movdi"
+ [(set (match_operand:DI 0 "nonimmediate_operand" "")
+ (match_operand:DI 1 "general_operand" ""))]
+ ""
+ "expand_move (operands, DImode);")
+
+(define_expand "movsf"
+ [(set (match_operand:SF 0 "nonimmediate_operand" "")
+ (match_operand:SF 1 "general_operand" ""))]
+ ""
+ "expand_move (operands, SFmode);")
+
+(define_expand "movdf"
+ [(set (match_operand:DF 0 "nonimmediate_operand" "")
+ (match_operand:DF 1 "general_operand" ""))]
+ ""
+ "expand_move (operands, DFmode);")
+
+(define_expand "movhi"
+ [(set (match_operand:HI 0 "nonimmediate_operand" "")
+ (match_operand:HI 1 "general_operand" ""))]
+ ""
+ "expand_move (operands, HImode);")
+
+(define_expand "movqi"
+ [(set (match_operand:QI 0 "nonimmediate_operand" "")
+ (match_operand:QI 1 "general_operand" ""))]
+ ""
+ " expand_move (operands, QImode); ")
+
+;; Some define_splits to break up SI/SFmode loads of immediate constants.
+
+(define_split
+ [(set (match_operand:SI 0 "register_operand" "")
+ (match_operand:SI 1 "symbolic_or_const_operand" ""))]
+ "reload_completed
+ /* Always split symbolic operands; split integer constants that are
+ too large for a single instruction. */
+ && (GET_CODE (operands[1]) != CONST_INT
+ || (INTVAL (operands[1]) < -32768
+ || INTVAL (operands[1]) >= 65536
+ || (INTVAL (operands[1]) >= 32768 && PREG_P (operands[0]))))"
+ [(set (match_dup 0) (high:SI (match_dup 1)))
+ (set (match_dup 0) (lo_sum:SI (match_dup 0) (match_dup 1)))]
+{
+ if (GET_CODE (operands[1]) == CONST_INT
+ && split_load_immediate (operands))
+ DONE;
+ /* ??? Do something about TARGET_LOW_64K. */
+})
+
+(define_split
+ [(set (match_operand:SF 0 "register_operand" "")
+ (match_operand:SF 1 "immediate_operand" ""))]
+ "reload_completed"
+ [(set (match_dup 2) (high:SI (match_dup 3)))
+ (set (match_dup 2) (lo_sum:SI (match_dup 2) (match_dup 3)))]
+{
+ long values;
+ REAL_VALUE_TYPE value;
+
+ if (GET_CODE (operands[1]) != CONST_DOUBLE)
+ abort ();
+
+ REAL_VALUE_FROM_CONST_DOUBLE (value, operands[1]);
+ REAL_VALUE_TO_TARGET_SINGLE (value, values);
+
+ operands[2] = gen_rtx_REG (SImode, true_regnum (operands[0]));
+ operands[3] = GEN_INT (trunc_int_for_mode (values, SImode));
+ if (values >= -32768 && values < 65536)
+ {
+ emit_move_insn (operands[2], operands[3]);
+ DONE;
+ }
+ if (split_load_immediate (operands + 2))
+ DONE;
+})
+
+;; Sadly, this can't be a proper named movstrict pattern, since the compiler
+;; expects to be able to use registers for operand 1.
+;; Note that the asm instruction is defined by the manual to take an unsigned
+;; constant, but it doesn't matter to the assembler, and the compiler only
+;; deals with sign-extended constants. Hence "Ksh".
+(define_insn "*movstricthi"
+ [(set (strict_low_part (match_operand:HI 0 "register_operand" "+x"))
+ (match_operand:HI 1 "immediate_operand" "Ksh"))]
+ ""
+ "%h0 = %1;"
+ [(set_attr "type" "mvi")
+ (set_attr "length" "4")])
+
+;; Sign and zero extensions
+
+(define_insn "extendhisi2"
+ [(set (match_operand:SI 0 "register_operand" "=d, d")
+ (sign_extend:SI (match_operand:HI 1 "nonimmediate_operand" "d, m")))]
+ ""
+ "@
+ %0 = %h1 (X);
+ %0 = W %h1 (X);"
+ [(set_attr "type" "alu0,mcld")])
+
+(define_insn "zero_extendhisi2"
+ [(set (match_operand:SI 0 "register_operand" "=d, d")
+ (zero_extend:SI (match_operand:HI 1 "nonimmediate_operand" "d, m")))]
+ ""
+ "@
+ %0 = %h1 (Z);
+ %0 = W%h1 (Z);"
+ [(set_attr "type" "alu0,mcld")])
+
+(define_insn "zero_extendbisi2"
+ [(set (match_operand:SI 0 "register_operand" "=d")
+ (zero_extend:SI (match_operand:BI 1 "nonimmediate_operand" "C")))]
+ ""
+ "%0 = %1;"
+ [(set_attr "type" "compare")])
+
+(define_insn "extendqihi2"
+ [(set (match_operand:HI 0 "register_operand" "=d, d")
+ (sign_extend:HI (match_operand:QI 1 "nonimmediate_operand" "m, d")))]
+ ""
+ "@
+ %0 = B %1 (X);
+ %0 = %T1 (X);"
+ [(set_attr "type" "mcld,alu0")])
+
+(define_insn "extendqisi2"
+ [(set (match_operand:SI 0 "register_operand" "=d, d")
+ (sign_extend:SI (match_operand:QI 1 "nonimmediate_operand" "m, d")))]
+ ""
+ "@
+ %0 = B %1 (X);
+ %0 = %T1 (X);"
+ [(set_attr "type" "mcld,alu0")])
+
+
+(define_insn "zero_extendqihi2"
+ [(set (match_operand:HI 0 "register_operand" "=d, d")
+ (zero_extend:HI (match_operand:QI 1 "nonimmediate_operand" "m, d")))]
+ ""
+ "@
+ %0 = B %1 (Z);
+ %0 = %T1 (Z);"
+ [(set_attr "type" "mcld,alu0")])
+
+
+(define_insn "zero_extendqisi2"
+ [(set (match_operand:SI 0 "register_operand" "=d, d")
+ (zero_extend:SI (match_operand:QI 1 "nonimmediate_operand" "m, d")))]
+ ""
+ "@
+ %0 = B %1 (Z);
+ %0 = %T1 (Z);"
+ [(set_attr "type" "mcld,alu0")])
+
+;; DImode logical operations
+
+(define_code_macro any_logical [and ior xor])
+(define_code_attr optab [(and "and")
+ (ior "ior")
+ (xor "xor")])
+(define_code_attr op [(and "&")
+ (ior "|")
+ (xor "^")])
+(define_code_attr high_result [(and "0")
+ (ior "%H1")
+ (xor "%H1")])
+
+(define_insn "<optab>di3"
+ [(set (match_operand:DI 0 "register_operand" "=d")
+ (any_logical:DI (match_operand:DI 1 "register_operand" "0")
+ (match_operand:DI 2 "register_operand" "d")))]
+ ""
+ "%0 = %1 <op> %2;\\n\\t%H0 = %H1 <op> %H2;"
+ [(set_attr "length" "4")])
+
+(define_insn "*<optab>di_zesidi_di"
+ [(set (match_operand:DI 0 "register_operand" "=d")
+ (any_logical:DI (zero_extend:DI
+ (match_operand:SI 2 "register_operand" "d"))
+ (match_operand:DI 1 "register_operand" "d")))]
+ ""
+ "%0 = %1 <op> %2;\\n\\t%H0 = <high_result>;"
+ [(set_attr "length" "4")])
+
+(define_insn "*<optab>di_sesdi_di"
+ [(set (match_operand:DI 0 "register_operand" "=d")
+ (any_logical:DI (sign_extend:DI
+ (match_operand:SI 2 "register_operand" "d"))
+ (match_operand:DI 1 "register_operand" "0")))
+ (clobber (match_scratch:SI 3 "=&d"))]
+ ""
+ "%0 = %1 <op> %2;\\n\\t%3 = %2;\\n\\t%3 >>>= 31;\\n\\t%H0 = %H1 <op> %3;"
+ [(set_attr "length" "8")])
+
+(define_insn "negdi2"
+ [(set (match_operand:DI 0 "register_operand" "=d")
+ (neg:DI (match_operand:DI 1 "register_operand" "d")))
+ (clobber (match_scratch:SI 2 "=&d"))
+ (clobber (reg:CC REG_CC))]
+ ""
+ "%2 = 0; %2 = %2 - %1; cc = ac0; cc = !cc; %2 = cc;\\n\\t%0 = -%1; %H0 = -%H1; %H0 = %H0 - %2;"
+ [(set_attr "length" "16")])
+
+(define_insn "one_cmpldi2"
+ [(set (match_operand:DI 0 "register_operand" "=d")
+ (not:DI (match_operand:DI 1 "register_operand" "d")))]
+ ""
+ "%0 = ~%1;\\n\\t%H0 = ~%H1;"
+ [(set_attr "length" "4")])
+
+;; DImode zero and sign extend patterns
+
+(define_insn_and_split "zero_extendsidi2"
+ [(set (match_operand:DI 0 "register_operand" "=d")
+ (zero_extend:DI (match_operand:SI 1 "register_operand" "d")))]
+ ""
+ "#"
+ "reload_completed"
+ [(set (match_dup 3) (const_int 0))]
+{
+ split_di (operands, 1, operands + 2, operands + 3);
+ if (REGNO (operands[0]) != REGNO (operands[1]))
+ emit_move_insn (operands[2], operands[1]);
+})
+
+(define_insn "zero_extendqidi2"
+ [(set (match_operand:DI 0 "register_operand" "=d")
+ (zero_extend:DI (match_operand:QI 1 "register_operand" "d")))]
+ ""
+ "%0 = %T1 (Z);\\n\\t%H0 = 0;"
+ [(set_attr "length" "4")])
+
+(define_insn "zero_extendhidi2"
+ [(set (match_operand:DI 0 "register_operand" "=d")
+ (zero_extend:DI (match_operand:HI 1 "register_operand" "d")))]
+ ""
+ "%0 = %h1 (Z);\\n\\t%H0 = 0;"
+ [(set_attr "length" "4")])
+
+(define_insn_and_split "extendsidi2"
+ [(set (match_operand:DI 0 "register_operand" "=d")
+ (sign_extend:DI (match_operand:SI 1 "register_operand" "d")))]
+ ""
+ "#"
+ "reload_completed"
+ [(set (match_dup 3) (match_dup 1))
+ (set (match_dup 3) (ashiftrt:SI (match_dup 3) (const_int 31)))]
+{
+ split_di (operands, 1, operands + 2, operands + 3);
+ if (REGNO (operands[0]) != REGNO (operands[1]))
+ emit_move_insn (operands[2], operands[1]);
+})
+
+(define_insn_and_split "extendqidi2"
+ [(set (match_operand:DI 0 "register_operand" "=d")
+ (sign_extend:DI (match_operand:QI 1 "register_operand" "d")))]
+ ""
+ "#"
+ "reload_completed"
+ [(set (match_dup 2) (sign_extend:SI (match_dup 1)))
+ (set (match_dup 3) (sign_extend:SI (match_dup 1)))
+ (set (match_dup 3) (ashiftrt:SI (match_dup 3) (const_int 31)))]
+{
+ split_di (operands, 1, operands + 2, operands + 3);
+})
+
+(define_insn_and_split "extendhidi2"
+ [(set (match_operand:DI 0 "register_operand" "=d")
+ (sign_extend:DI (match_operand:HI 1 "register_operand" "d")))]
+ ""
+ "#"
+ "reload_completed"
+ [(set (match_dup 2) (sign_extend:SI (match_dup 1)))
+ (set (match_dup 3) (sign_extend:SI (match_dup 1)))
+ (set (match_dup 3) (ashiftrt:SI (match_dup 3) (const_int 31)))]
+{
+ split_di (operands, 1, operands + 2, operands + 3);
+})
+
+;; DImode arithmetic operations
+
+(define_insn "adddi3"
+ [(set (match_operand:DI 0 "register_operand" "=&d,&d,&d")
+ (plus:DI (match_operand:DI 1 "register_operand" "%0,0,0")
+ (match_operand:DI 2 "nonmemory_operand" "Kn7,Ks7,d")))
+ (clobber (match_scratch:SI 3 "=&d,&d,&d"))
+ (clobber (reg:CC 34))]
+ ""
+ "@
+ %0 += %2; cc = ac0; %3 = cc; %H0 += -1; %H0 = %H0 + %3;
+ %0 += %2; cc = ac0; %3 = cc; %H0 = %H0 + %3;
+ %0 = %0 + %2; cc = ac0; %3 = cc; %H0 = %H0 + %H2; %H0 = %H0 + %3;"
+ [(set_attr "type" "alu0")
+ (set_attr "length" "10,8,10")])
+
+(define_insn "subdi3"
+ [(set (match_operand:DI 0 "register_operand" "=&d")
+ (minus:DI (match_operand:DI 1 "register_operand" "0")
+ (match_operand:DI 2 "register_operand" "d")))
+ (clobber (reg:CC 34))]
+ ""
+ "%0 = %1-%2;\\n\\tcc = ac0;\\n\\t%H0 = %H1-%H2;\\n\\tif cc jump 1f;\\n\\t%H0 += -1;\\n\\t1:"
+ [(set_attr "length" "10")])
+
+(define_insn "*subdi_di_zesidi"
+ [(set (match_operand:DI 0 "register_operand" "=d")
+ (minus:DI (match_operand:DI 1 "register_operand" "0")
+ (zero_extend:DI
+ (match_operand:SI 2 "register_operand" "d"))))
+ (clobber (match_scratch:SI 3 "=&d"))
+ (clobber (reg:CC 34))]
+ ""
+ "%0 = %1 - %2;\\n\\tcc = ac0;\\n\\tcc = ! cc;\\n\\t%3 = cc;\\n\\t%H0 = %H1 - %3;"
+ [(set_attr "length" "10")])
+
+(define_insn "*subdi_zesidi_di"
+ [(set (match_operand:DI 0 "register_operand" "=d")
+ (minus:DI (zero_extend:DI
+ (match_operand:SI 2 "register_operand" "d"))
+ (match_operand:DI 1 "register_operand" "0")))
+ (clobber (match_scratch:SI 3 "=&d"))
+ (clobber (reg:CC 34))]
+ ""
+ "%0 = %2 - %1;\\n\\tcc = ac0;\\n\\tcc = ! cc;\\n\\t%3 = cc;\\n\\t%3 = -%3;\\n\\t%H0 = %3 - %H1"
+ [(set_attr "length" "12")])
+
+(define_insn "*subdi_di_sesidi"
+ [(set (match_operand:DI 0 "register_operand" "=d")
+ (minus:DI (match_operand:DI 1 "register_operand" "0")
+ (sign_extend:DI
+ (match_operand:SI 2 "register_operand" "d"))))
+ (clobber (match_scratch:SI 3 "=&d"))
+ (clobber (reg:CC 34))]
+ ""
+ "%0 = %1 - %2;\\n\\tcc = ac0;\\n\\t%3 = %2;\\n\\t%3 >>>= 31;\\n\\t%H0 = %H1 - %3;\\n\\tif cc jump 1f;\\n\\t%H0 += -1;\\n\\t1:"
+ [(set_attr "length" "14")])
+
+(define_insn "*subdi_sesidi_di"
+ [(set (match_operand:DI 0 "register_operand" "=d")
+ (minus:DI (sign_extend:DI
+ (match_operand:SI 2 "register_operand" "d"))
+ (match_operand:DI 1 "register_operand" "0")))
+ (clobber (match_scratch:SI 3 "=&d"))
+ (clobber (reg:CC 34))]
+ ""
+ "%0 = %2 - %1;\\n\\tcc = ac0;\\n\\t%3 = %2;\\n\\t%3 >>>= 31;\\n\\t%H0 = %3 - %H1;\\n\\tif cc jump 1f;\\n\\t%H0 += -1;\\n\\t1:"
+ [(set_attr "length" "14")])
+
+;; Combined shift/add instructions
+
+(define_insn ""
+ [(set (match_operand:SI 0 "register_operand" "=a,d")
+ (ashift:SI (plus:SI (match_operand:SI 1 "register_operand" "%0,0")
+ (match_operand:SI 2 "register_operand" "a,d"))
+ (match_operand:SI 3 "pos_scale_operand" "P1P2,P1P2")))]
+ ""
+ "%0 = (%0 + %2) << %3;" /* "shadd %0,%2,%3;" */
+ [(set_attr "type" "alu0")])
+
+(define_insn ""
+ [(set (match_operand:SI 0 "register_operand" "=a")
+ (plus:SI (match_operand:SI 1 "register_operand" "a")
+ (mult:SI (match_operand:SI 2 "register_operand" "a")
+ (match_operand:SI 3 "scale_by_operand" "i"))))]
+ ""
+ "%0 = %1 + (%2 << %X3);"
+ [(set_attr "type" "alu0")])
+
+(define_insn ""
+ [(set (match_operand:SI 0 "register_operand" "=a")
+ (plus:SI (match_operand:SI 1 "register_operand" "a")
+ (ashift:SI (match_operand:SI 2 "register_operand" "a")
+ (match_operand:SI 3 "pos_scale_operand" "i"))))]
+ ""
+ "%0 = %1 + (%2 << %3);"
+ [(set_attr "type" "alu0")])
+
+(define_insn ""
+ [(set (match_operand:SI 0 "register_operand" "=a")
+ (plus:SI (mult:SI (match_operand:SI 1 "register_operand" "a")
+ (match_operand:SI 2 "scale_by_operand" "i"))
+ (match_operand:SI 3 "register_operand" "a")))]
+ ""
+ "%0 = %3 + (%1 << %X2);"
+ [(set_attr "type" "alu0")])
+
+(define_insn ""
+ [(set (match_operand:SI 0 "register_operand" "=a")
+ (plus:SI (ashift:SI (match_operand:SI 1 "register_operand" "a")
+ (match_operand:SI 2 "pos_scale_operand" "i"))
+ (match_operand:SI 3 "register_operand" "a")))]
+ ""
+ "%0 = %3 + (%1 << %2);"
+ [(set_attr "type" "alu0")])
+
+(define_insn "mulhisi3"
+ [(set (match_operand:SI 0 "register_operand" "=d")
+ (mult:SI (sign_extend:SI (match_operand:HI 1 "register_operand" "%d"))
+ (sign_extend:SI (match_operand:HI 2 "register_operand" "d"))))]
+ ""
+ "%0 = %h1 * %h2 (IS);"
+ [(set_attr "type" "dsp32")])
+
+(define_insn "umulhisi3"
+ [(set (match_operand:SI 0 "register_operand" "=d")
+ (mult:SI (zero_extend:SI (match_operand:HI 1 "register_operand" "%d"))
+ (zero_extend:SI (match_operand:HI 2 "register_operand" "d"))))]
+ ""
+ "%0 = %h1 * %h2 (FU);"
+ [(set_attr "type" "dsp32")])
+
+;; The processor also supports ireg += mreg or ireg -= mreg, but these
+;; are unusable if we don't ensure that the corresponding lreg is zero.
+;; The same applies to the add/subtract constant versions involving
+;; iregs
+
+(define_insn "addsi3"
+ [(set (match_operand:SI 0 "register_operand" "=ad,a,d")
+ (plus:SI (match_operand:SI 1 "register_operand" "%0, a,d")
+ (match_operand:SI 2 "reg_or_7bit_operand" "Ks7, a,d")))]
+ ""
+ "@
+ %0 += %2;
+ %0 = %1 + %2;
+ %0 = %1 + %2;"
+ [(set_attr "type" "alu0")
+ (set_attr "length" "2,2,2")])
+
+(define_expand "subsi3"
+ [(set (match_operand:SI 0 "register_operand" "")
+ (minus:SI (match_operand:SI 1 "register_operand" "")
+ (match_operand:SI 2 "reg_or_7bit_operand" "")))]
+ ""
+ "")
+
+(define_insn ""
+ [(set (match_operand:SI 0 "register_operand" "=da,d,a")
+ (minus:SI (match_operand:SI 1 "register_operand" "0,d,0")
+ (match_operand:SI 2 "reg_or_7bit_operand" "Ks7,d,a")))]
+ "GET_CODE (operands[2]) != CONST_INT || INTVAL (operands[2]) != -64"
+{
+ static const char *const strings_subsi3[] = {
+ "%0 += -%2;",
+ "%0 = %1 - %2;",
+ "%0 -= %2;",
+ };
+
+ if (CONSTANT_P (operands[2]) && INTVAL (operands[2]) < 0) {
+ rtx tmp_op = operands[2];
+ operands[2] = GEN_INT (-INTVAL (operands[2]));
+ output_asm_insn ("%0 += %2;", operands);
+ operands[2] = tmp_op;
+ return "";
+ }
+
+ return strings_subsi3[which_alternative];
+}
+ [(set_attr "type" "alu0")])
+
+;; Bit test instructions
+
+(define_insn "*not_bittst"
+ [(set (match_operand:BI 0 "cc_operand" "=C")
+ (eq:BI (zero_extract:SI (match_operand:SI 1 "register_operand" "d")
+ (const_int 1)
+ (match_operand:SI 2 "immediate_operand" "Ku5"))
+ (const_int 0)))]
+ ""
+ "cc = !BITTST (%1,%2);"
+ [(set_attr "type" "alu0")])
+
+(define_insn "*bittst"
+ [(set (match_operand:BI 0 "cc_operand" "=C")
+ (ne:BI (zero_extract:SI (match_operand:SI 1 "register_operand" "d")
+ (const_int 1)
+ (match_operand:SI 2 "immediate_operand" "Ku5"))
+ (const_int 0)))]
+ ""
+ "cc = BITTST (%1,%2);"
+ [(set_attr "type" "alu0")])
+
+(define_insn_and_split "*bit_extract"
+ [(set (match_operand:SI 0 "register_operand" "=d")
+ (zero_extract:SI (match_operand:SI 1 "register_operand" "d")
+ (const_int 1)
+ (match_operand:SI 2 "immediate_operand" "Ku5")))
+ (clobber (reg:BI REG_CC))]
+ ""
+ "#"
+ ""
+ [(set (reg:BI REG_CC)
+ (ne:BI (zero_extract:SI (match_dup 1) (const_int 1) (match_dup 2))
+ (const_int 0)))
+ (set (match_dup 0)
+ (ne:SI (reg:BI REG_CC) (const_int 0)))])
+
+(define_insn_and_split "*not_bit_extract"
+ [(set (match_operand:SI 0 "register_operand" "=d")
+ (zero_extract:SI (not:SI (match_operand:SI 1 "register_operand" "d"))
+ (const_int 1)
+ (match_operand:SI 2 "immediate_operand" "Ku5")))
+ (clobber (reg:BI REG_CC))]
+ ""
+ "#"
+ ""
+ [(set (reg:BI REG_CC)
+ (eq:BI (zero_extract:SI (match_dup 1) (const_int 1) (match_dup 2))
+ (const_int 0)))
+ (set (match_dup 0)
+ (ne:SI (reg:BI REG_CC) (const_int 0)))])
+
+(define_insn "*andsi_insn"
+ [(set (match_operand:SI 0 "register_operand" "=d,d,d,d")
+ (and:SI (match_operand:SI 1 "register_operand" "%0,d,d,d")
+ (match_operand:SI 2 "rhs_andsi3_operand" "L,M1,M2,d")))]
+ ""
+ "@
+ BITCLR (%0,%Y2);
+ %0 = %T1 (Z);
+ %0 = %h1 (Z);
+ %0 = %1 & %2;"
+ [(set_attr "type" "alu0")])
+
+(define_expand "andsi3"
+ [(set (match_operand:SI 0 "register_operand" "")
+ (and:SI (match_operand:SI 1 "register_operand" "")
+ (match_operand:SI 2 "general_operand" "")))]
+ ""
+{
+ if (highbits_operand (operands[2], SImode))
+ {
+ operands[2] = GEN_INT (exact_log2 (-INTVAL (operands[2])));
+ emit_insn (gen_ashrsi3 (operands[0], operands[1], operands[2]));
+ emit_insn (gen_ashlsi3 (operands[0], operands[0], operands[2]));
+ DONE;
+ }
+ if (! rhs_andsi3_operand (operands[2], SImode))
+ operands[2] = force_reg (SImode, operands[2]);
+})
+
+(define_insn "iorsi3"
+ [(set (match_operand:SI 0 "register_operand" "=d,d")
+ (ior:SI (match_operand:SI 1 "register_operand" "%0,d")
+ (match_operand:SI 2 "regorlog2_operand" "J,d")))]
+ ""
+ "@
+ BITSET (%0, %X2);
+ %0 = %1 | %2;"
+ [(set_attr "type" "alu0")])
+
+(define_insn "xorsi3"
+ [(set (match_operand:SI 0 "register_operand" "=d,d")
+ (xor:SI (match_operand:SI 1 "register_operand" "%0,d")
+ (match_operand:SI 2 "regorlog2_operand" "J,d")))]
+ ""
+ "@
+ BITTGL (%0, %X2);
+ %0 = %1 ^ %2;"
+ [(set_attr "type" "alu0")])
+
+(define_insn "smaxsi3"
+ [(set (match_operand:SI 0 "register_operand" "=d")
+ (smax:SI (match_operand:SI 1 "register_operand" "d")
+ (match_operand:SI 2 "register_operand" "d")))]
+ ""
+ "%0 =max(%1,%2);"
+ [(set_attr "type" "dsp32")])
+
+(define_insn "sminsi3"
+ [(set (match_operand:SI 0 "register_operand" "=d")
+ (smin:SI (match_operand:SI 1 "register_operand" "d")
+ (match_operand:SI 2 "register_operand" "d")))]
+ ""
+ "%0 =min(%1,%2);"
+ [(set_attr "type" "dsp32")])
+
+(define_insn "abssi2"
+ [(set (match_operand:SI 0 "register_operand" "=d")
+ (abs:SI (match_operand:SI 1 "register_operand" " d")))]
+ ""
+ "%0 =abs %1;"
+ [(set_attr "type" "dsp32")])
+
+
+(define_insn "negsi2"
+ [(set (match_operand:SI 0 "register_operand" "=d")
+ (neg:SI (match_operand:SI 1 "register_operand" " d")))]
+ ""
+ "%0 =-%1;"
+ [(set_attr "type" "alu0")])
+
+(define_insn "one_cmplsi2"
+ [(set (match_operand:SI 0 "register_operand" "=d")
+ (not:SI (match_operand:SI 1 "register_operand" " d")))]
+ ""
+ "%0 =~%1;"
+ [(set_attr "type" "alu0")])
+
+(define_insn "mulsi3"
+ [(set (match_operand:SI 0 "register_operand" "=d")
+ (mult:SI (match_operand:SI 1 "register_operand" "%0")
+ (match_operand:SI 2 "register_operand" "d")))]
+ ""
+ "%0 *=%2;"
+ [(set_attr "type" "mult")])
+
+(define_expand "ashlsi3"
+ [(set (match_operand:SI 0 "register_operand" "")
+ (ashift:SI (match_operand:SI 1 "register_operand" "")
+ (match_operand:SI 2 "nonmemory_operand" "")))]
+ ""
+{
+ if (GET_CODE (operands[2]) == CONST_INT
+ && ((unsigned HOST_WIDE_INT) INTVAL (operands[2])) > 31)
+ {
+ emit_insn (gen_movsi (operands[0], const0_rtx));
+ DONE;
+ }
+})
+
+(define_insn_and_split "*ashlsi3_insn"
+ [(set (match_operand:SI 0 "register_operand" "=d,a,a,a")
+ (ashift:SI (match_operand:SI 1 "register_operand" "0,a,a,a")
+ (match_operand:SI 2 "nonmemory_operand" "dKu5,P1,P2,?P3P4")))]
+ ""
+ "@
+ %0 <<= %2;
+ %0 = %1 + %1;
+ %0 = %1 << %2;
+ #"
+ "PREG_P (operands[0]) && INTVAL (operands[2]) > 2"
+ [(set (match_dup 0) (ashift:SI (match_dup 1) (const_int 2)))
+ (set (match_dup 0) (ashift:SI (match_dup 0) (match_dup 3)))]
+ "operands[3] = GEN_INT (INTVAL (operands[2]) - 2);"
+ [(set_attr "type" "shft")])
+
+(define_insn "ashrsi3"
+ [(set (match_operand:SI 0 "register_operand" "=d")
+ (ashiftrt:SI (match_operand:SI 1 "register_operand" "0")
+ (match_operand:SI 2 "nonmemory_operand" "dKu5")))]
+ ""
+ "%0 >>>= %2;"
+ [(set_attr "type" "shft")])
+
+(define_insn "lshrsi3"
+ [(set (match_operand:SI 0 "register_operand" "=d,a")
+ (lshiftrt:SI (match_operand:SI 1 "register_operand" " 0,a")
+ (match_operand:SI 2 "nonmemory_operand" "dKu5,P1P2")))]
+ ""
+ "@
+ %0 >>= %2;
+ %0 = %1 >> %2;"
+ [(set_attr "type" "shft")])
+
+;; A pattern to reload the equivalent of
+;; (set (Dreg) (plus (FP) (large_constant)))
+;; or
+;; (set (dagreg) (plus (FP) (arbitrary_constant)))
+;; using a scratch register
+(define_expand "reload_insi"
+ [(parallel [(set (match_operand:SI 0 "register_operand" "=w")
+ (match_operand:SI 1 "fp_plus_const_operand" ""))
+ (clobber (match_operand:SI 2 "register_operand" "=&a"))])]
+ ""
+{
+ rtx fp_op = XEXP (operands[1], 0);
+ rtx const_op = XEXP (operands[1], 1);
+ rtx primary = operands[0];
+ rtx scratch = operands[2];
+
+ emit_move_insn (scratch, const_op);
+ emit_insn (gen_addsi3 (scratch, scratch, fp_op));
+ emit_move_insn (primary, scratch);
+ DONE;
+})
+
+;; Jump instructions
+
+(define_insn "jump"
+ [(set (pc)
+ (label_ref (match_operand 0 "" "")))]
+ ""
+{
+ if (get_attr_length (insn) == 2)
+ return "jump.s %0;";
+ else
+ return "jump.l %0;";
+}
+ [(set_attr "type" "br")])
+
+(define_insn "indirect_jump"
+ [(set (pc)
+ (match_operand:SI 0 "register_operand" "a"))]
+ ""
+ "jump (%0);"
+ [(set_attr "type" "misc")])
+
+(define_expand "tablejump"
+ [(parallel [(set (pc) (match_operand:SI 0 "register_operand" "a"))
+ (use (label_ref (match_operand 1 "" "")))])]
+ ""
+{
+ /* In PIC mode, the table entries are stored PC relative.
+ Convert the relative address to an absolute address. */
+ if (flag_pic)
+ {
+ rtx op1 = gen_rtx_LABEL_REF (Pmode, operands[1]);
+
+ operands[0] = expand_simple_binop (Pmode, PLUS, operands[0],
+ op1, NULL_RTX, 0, OPTAB_DIRECT);
+ }
+})
+
+(define_insn "*tablejump_internal"
+ [(set (pc) (match_operand:SI 0 "register_operand" "a"))
+ (use (label_ref (match_operand 1 "" "")))]
+ ""
+ "jump (%0);"
+ [(set_attr "type" "misc")])
+
+;; Call instructions..
+
+(define_expand "call"
+ [(call (match_operand:SI 0 "" "")
+ (match_operand 1 "" ""))]
+ ""
+ "bfin_expand_call (NULL_RTX, operands[0], operands[1], 0); DONE;")
+
+(define_expand "sibcall"
+ [(parallel [(call (match_operand:SI 0 "" "")
+ (match_operand 1 "" ""))
+ (return)])]
+ ""
+ "bfin_expand_call (NULL_RTX, operands[0], operands[1], 1); DONE;")
+
+(define_expand "call_value"
+ [(set (match_operand 0 "register_operand" "")
+ (call (match_operand:SI 1 "" "")
+ (match_operand 2 "" "")))]
+ ""
+ "bfin_expand_call (operands[0], operands[1], operands[2], 0); DONE;")
+
+(define_expand "sibcall_value"
+ [(parallel [(set (match_operand 0 "register_operand" "")
+ (call (match_operand:SI 1 "" "")
+ (match_operand 2 "" "")))
+ (return)])]
+ ""
+ "bfin_expand_call (operands[0], operands[1], operands[2], 1); DONE;")
+
+(define_insn "*call_insn"
+ [(call (mem:SI (match_operand:SI 0 "call_insn_operand" "a,Q"))
+ (match_operand 1 "general_operand" "g,g"))]
+ "! SIBLING_CALL_P (insn)
+ && (GET_CODE (operands[0]) == SYMBOL_REF || GET_CODE (operands[0]) == REG)"
+ "@
+ call (%0);
+ call %G0;"
+ [(set_attr "type" "call")
+ (set_attr "length" "2,4")])
+
+(define_insn "*sibcall_insn"
+ [(call (mem:SI (match_operand:SI 0 "call_insn_operand" "z,Q"))
+ (match_operand 1 "general_operand" "g,g"))
+ (return)]
+ "SIBLING_CALL_P (insn)
+ && (GET_CODE (operands[0]) == SYMBOL_REF || GET_CODE (operands[0]) == REG)"
+ "@
+ jump (%0);
+ jump.l %G0;"
+ [(set_attr "type" "br")
+ (set_attr "length" "2,4")])
+
+(define_insn "*call_value_insn"
+ [(set (match_operand 0 "register_operand" "=d,d")
+ (call (mem:SI (match_operand:SI 1 "call_insn_operand" "a,Q"))
+ (match_operand 2 "general_operand" "g,g")))]
+ "! SIBLING_CALL_P (insn)
+ && (GET_CODE (operands[0]) == SYMBOL_REF || GET_CODE (operands[0]) == REG)"
+ "@
+ call (%1);
+ call %G1;"
+ [(set_attr "type" "call")
+ (set_attr "length" "2,4")])
+
+(define_insn "*sibcall_value_insn"
+ [(set (match_operand 0 "register_operand" "=d,d")
+ (call (mem:SI (match_operand:SI 1 "call_insn_operand" "z,Q"))
+ (match_operand 2 "general_operand" "g,g")))
+ (return)]
+ "SIBLING_CALL_P (insn)
+ && (GET_CODE (operands[0]) == SYMBOL_REF || GET_CODE (operands[0]) == REG)"
+ "@
+ jump (%1);
+ jump.l %G1;"
+ [(set_attr "type" "br")
+ (set_attr "length" "2,4")])
+
+;; Block move patterns
+
+;; We cheat. This copies one more word than operand 2 indicates.
+
+(define_insn "rep_movsi"
+ [(set (match_operand:SI 0 "register_operand" "=&a")
+ (plus:SI (plus:SI (match_operand:SI 3 "register_operand" "0")
+ (ashift:SI (match_operand:SI 2 "register_operand" "a")
+ (const_int 2)))
+ (const_int 4)))
+ (set (match_operand:SI 1 "register_operand" "=&b")
+ (plus:SI (plus:SI (match_operand:SI 4 "register_operand" "1")
+ (ashift:SI (match_dup 2) (const_int 2)))
+ (const_int 4)))
+ (set (mem:BLK (match_dup 3))
+ (mem:BLK (match_dup 4)))
+ (use (match_dup 2))
+ (clobber (match_scratch:HI 5 "=&d"))]
+ ""
+ "lsetup (1f, 1f) LC1 = %2; %5 = [%4++]; 1: MNOP || [%3++] = %5 || %5 = [%4++]; [%3++] = %5;"
+ [(set_attr "type" "misc")
+ (set_attr "length" "16")])
+
+(define_insn "rep_movhi"
+ [(set (match_operand:SI 0 "register_operand" "=&a")
+ (plus:SI (plus:SI (match_operand:SI 3 "register_operand" "0")
+ (ashift:SI (match_operand:SI 2 "register_operand" "a")
+ (const_int 1)))
+ (const_int 2)))
+ (set (match_operand:SI 1 "register_operand" "=&b")
+ (plus:SI (plus:SI (match_operand:SI 4 "register_operand" "1")
+ (ashift:SI (match_dup 2) (const_int 1)))
+ (const_int 2)))
+ (set (mem:BLK (match_dup 3))
+ (mem:BLK (match_dup 4)))
+ (use (match_dup 2))
+ (clobber (match_scratch:HI 5 "=&d"))]
+ ""
+ "lsetup (1f, 1f) LC1 = %2; %h5 = W[%4++]; 1: MNOP || W [%3++] = %5 || %h5 = W [%4++]; W [%3++] = %5;"
+ [(set_attr "type" "misc")
+ (set_attr "length" "16")])
+
+(define_expand "movstrsi"
+ [(match_operand:BLK 0 "general_operand" "")
+ (match_operand:BLK 1 "general_operand" "")
+ (match_operand:SI 2 "const_int_operand" "")
+ (match_operand:SI 3 "const_int_operand" "")]
+ ""
+{
+ if (bfin_expand_strmov (operands[0], operands[1], operands[2], operands[3]))
+ DONE;
+ FAIL;
+})
+
+;; Conditional branch patterns
+;; The Blackfin has only few condition codes: eq, lt, lte, ltu, leu
+
+;; The only outcome of this pattern is that global variables
+;; bfin_compare_op[01] are set for use in bcond patterns.
+
+(define_expand "cmpbi"
+ [(set (cc0) (compare (match_operand:BI 0 "register_operand" "")
+ (match_operand:BI 1 "immediate_operand" "")))]
+ ""
+{
+ bfin_compare_op0 = operands[0];
+ bfin_compare_op1 = operands[1];
+ DONE;
+})
+
+(define_expand "cmpsi"
+ [(set (cc0) (compare (match_operand:SI 0 "register_operand" "")
+ (match_operand:SI 1 "nonmemory_operand" "")))]
+ ""
+{
+ bfin_compare_op0 = operands[0];
+ bfin_compare_op1 = operands[1];
+ DONE;
+})
+
+(define_insn ""
+ [(set (match_operand:BI 0 "cc_operand" "=C,C")
+ (eq:BI (match_operand:SI 1 "register_operand" "d,a")
+ (match_operand:SI 2 "nonmemory_operand" "dKs3,aKs3")))]
+ ""
+ "cc =%1==%2;"
+ [(set_attr "type" "compare")])
+
+(define_insn ""
+ [(set (match_operand:BI 0 "cc_operand" "=C,C")
+ (ne:BI (match_operand:SI 1 "register_operand" "d,a")
+ (match_operand:SI 2 "nonmemory_operand" "dKs3,aKs3")))]
+ "0"
+ "cc =%1!=%2;"
+ [(set_attr "type" "compare")])
+
+(define_insn ""
+ [(set (match_operand:BI 0 "cc_operand" "=C,C")
+ (lt:BI (match_operand:SI 1 "register_operand" "d,a")
+ (match_operand:SI 2 "nonmemory_operand" "dKs3,aKs3")))]
+ ""
+ "cc =%1<%2;"
+ [(set_attr "type" "compare")])
+
+(define_insn ""
+ [(set (match_operand:BI 0 "cc_operand" "=C,C")
+ (le:BI (match_operand:SI 1 "register_operand" "d,a")
+ (match_operand:SI 2 "nonmemory_operand" "dKs3,aKs3")))]
+ ""
+ "cc =%1<=%2;"
+ [(set_attr "type" "compare")])
+
+(define_insn ""
+ [(set (match_operand:BI 0 "cc_operand" "=C,C")
+ (leu:BI (match_operand:SI 1 "register_operand" "d,a")
+ (match_operand:SI 2 "nonmemory_operand" "dKu3,aKu3")))]
+ ""
+ "cc =%1<=%2 (iu);"
+ [(set_attr "type" "compare")])
+
+(define_insn ""
+ [(set (match_operand:BI 0 "cc_operand" "=C,C")
+ (ltu:BI (match_operand:SI 1 "register_operand" "d,a")
+ (match_operand:SI 2 "nonmemory_operand" "dKu3,aKu3")))]
+ ""
+ "cc =%1<%2 (iu);"
+ [(set_attr "type" "compare")])
+
+(define_expand "beq"
+ [(set (match_dup 1) (match_dup 2))
+ (set (pc)
+ (if_then_else (match_dup 3)
+ (label_ref (match_operand 0 "" ""))
+ (pc)))]
+ ""
+{
+ rtx op0 = bfin_compare_op0, op1 = bfin_compare_op1;
+ operands[1] = bfin_cc_rtx; /* hard register: CC */
+ operands[2] = gen_rtx_EQ (BImode, op0, op1);
+ /* If we have a BImode input, then we already have a compare result, and
+ do not need to emit another comparison. */
+ if (GET_MODE (bfin_compare_op0) == BImode)
+ {
+ if (bfin_compare_op1 == const0_rtx)
+ {
+ emit_insn (gen_cbranchbi4 (operands[2], op0, op1,
+ operands[0]));
+ DONE;
+ }
+ else
+ abort ();
+ }
+
+ operands[3] = gen_rtx_NE (BImode, operands[1], const0_rtx);
+})
+
+(define_expand "bne"
+ [(set (match_dup 1) (match_dup 2))
+ (set (pc)
+ (if_then_else (match_dup 3)
+ (label_ref (match_operand 0 "" ""))
+ (pc)))]
+ ""
+{
+ rtx op0 = bfin_compare_op0, op1 = bfin_compare_op1;
+ /* If we have a BImode input, then we already have a compare result, and
+ do not need to emit another comparison. */
+ if (GET_MODE (bfin_compare_op0) == BImode)
+ {
+ if (bfin_compare_op1 == const0_rtx)
+ {
+ rtx cmp = gen_rtx_NE (BImode, op0, op1);
+ emit_insn (gen_cbranchbi4 (cmp, op0, op1, operands[0]));
+ DONE;
+ }
+ else
+ abort ();
+ }
+
+ operands[1] = bfin_cc_rtx; /* hard register: CC */
+ operands[2] = gen_rtx_EQ (BImode, op0, op1);
+ operands[3] = gen_rtx_EQ (BImode, operands[1], const0_rtx);
+})
+
+(define_expand "bgt"
+ [(set (match_dup 1) (match_dup 2))
+ (set (pc)
+ (if_then_else (match_dup 3)
+ (label_ref (match_operand 0 "" ""))
+ (pc)))]
+ ""
+{
+ operands[1] = bfin_cc_rtx;
+ operands[2] = gen_rtx_LE (BImode, bfin_compare_op0, bfin_compare_op1);
+ operands[3] = gen_rtx_EQ (BImode, operands[1], const0_rtx);
+})
+
+(define_expand "bgtu"
+ [(set (match_dup 1) (match_dup 2))
+ (set (pc)
+ (if_then_else (match_dup 3)
+ (label_ref (match_operand 0 "" ""))
+ (pc)))]
+ ""
+{
+ operands[1] = bfin_cc_rtx;
+ operands[2] = gen_rtx_LEU (BImode, bfin_compare_op0, bfin_compare_op1);
+ operands[3] = gen_rtx_EQ (BImode, operands[1], const0_rtx);
+})
+
+(define_expand "blt"
+ [(set (match_dup 1) (match_dup 2))
+ (set (pc)
+ (if_then_else (match_dup 3)
+ (label_ref (match_operand 0 "" ""))
+ (pc)))]
+ ""
+{
+ operands[1] = bfin_cc_rtx;
+ operands[2] = gen_rtx_LT (BImode, bfin_compare_op0, bfin_compare_op1);
+ operands[3] = gen_rtx_NE (BImode, operands[1], const0_rtx);
+})
+
+(define_expand "bltu"
+ [(set (match_dup 1) (match_dup 2))
+ (set (pc)
+ (if_then_else (match_dup 3)
+ (label_ref (match_operand 0 "" ""))
+ (pc)))]
+ ""
+{
+ operands[1] = bfin_cc_rtx;
+ operands[2] = gen_rtx_LTU (BImode, bfin_compare_op0, bfin_compare_op1);
+ operands[3] = gen_rtx_NE (BImode, operands[1], const0_rtx);
+})
+
+
+(define_expand "bge"
+ [(set (match_dup 1) (match_dup 2))
+ (set (pc)
+ (if_then_else (match_dup 3)
+ (label_ref (match_operand 0 "" ""))
+ (pc)))]
+ ""
+{
+ operands[1] = bfin_cc_rtx;
+ operands[2] = gen_rtx_LT (BImode, bfin_compare_op0, bfin_compare_op1);
+ operands[3] = gen_rtx_EQ (BImode, operands[1], const0_rtx);
+})
+
+(define_expand "bgeu"
+ [(set (match_dup 1) (match_dup 2))
+ (set (pc)
+ (if_then_else (match_dup 3)
+ (label_ref (match_operand 0 "" ""))
+ (pc)))]
+ ""
+{
+ operands[1] = bfin_cc_rtx;
+ operands[2] = gen_rtx_LTU (BImode, bfin_compare_op0, bfin_compare_op1);
+ operands[3] = gen_rtx_EQ (BImode, operands[1], const0_rtx);
+})
+
+(define_expand "ble"
+ [(set (match_dup 1) (match_dup 2))
+ (set (pc)
+ (if_then_else (match_dup 3)
+ (label_ref (match_operand 0 "" ""))
+ (pc)))]
+ ""
+{
+ operands[1] = bfin_cc_rtx;
+ operands[2] = gen_rtx_LE (BImode, bfin_compare_op0, bfin_compare_op1);
+ operands[3] = gen_rtx_NE (BImode, operands[1], const0_rtx);
+})
+
+(define_expand "bleu"
+ [(set (match_dup 1) (match_dup 2))
+ (set (pc)
+ (if_then_else (match_dup 3)
+ (label_ref (match_operand 0 "" ""))
+ (pc)))
+ ]
+ ""
+{
+ operands[1] = bfin_cc_rtx;
+ operands[2] = gen_rtx_LEU (BImode, bfin_compare_op0, bfin_compare_op1);
+ operands[3] = gen_rtx_NE (BImode, operands[1], const0_rtx);
+})
+
+(define_insn "cbranchbi4"
+ [(set (pc)
+ (if_then_else
+ (match_operator 0 "bfin_cbranch_operator"
+ [(match_operand:BI 1 "cc_operand" "C")
+ (match_operand:BI 2 "immediate_operand" "P0")])
+ (label_ref (match_operand 3 "" ""))
+ (pc)))]
+ ""
+{
+ asm_conditional_branch (insn, operands, 0, 0);
+ return "";
+}
+ [(set_attr "type" "brcc")])
+
+;; Special cbranch patterns to deal with the speculative load problem - see
+;; bfin_reorg for details.
+
+(define_insn "cbranch_predicted_taken"
+ [(set (pc)
+ (if_then_else
+ (match_operator 0 "bfin_cbranch_operator"
+ [(match_operand:BI 1 "cc_operand" "C")
+ (match_operand:BI 2 "immediate_operand" "P0")])
+ (label_ref (match_operand 3 "" ""))
+ (pc)))
+ (unspec [(const_int 0)] UNSPEC_CBRANCH_TAKEN)]
+ ""
+{
+ asm_conditional_branch (insn, operands, 0, 1);
+ return "";
+}
+ [(set_attr "type" "brcc")])
+
+(define_insn "cbranch_with_nops"
+ [(set (pc)
+ (if_then_else
+ (match_operator 0 "bfin_cbranch_operator"
+ [(match_operand:BI 1 "cc_operand" "C")
+ (match_operand:BI 2 "immediate_operand" "P0")])
+ (label_ref (match_operand 3 "" ""))
+ (pc)))
+ (unspec [(match_operand 4 "immediate_operand" "")] UNSPEC_CBRANCH_NOPS)]
+ "reload_completed"
+{
+ asm_conditional_branch (insn, operands, INTVAL (operands[4]), 0);
+ return "";
+}
+ [(set_attr "type" "brcc")
+ (set_attr "length" "6")])
+
+;; setcc insns. */
+(define_expand "seq"
+ [(set (match_dup 1) (eq:BI (match_dup 2) (match_dup 3)))
+ (set (match_operand:SI 0 "register_operand" "")
+ (ne:SI (match_dup 1) (const_int 0)))]
+ ""
+{
+ operands[2] = bfin_compare_op0;
+ operands[3] = bfin_compare_op1;
+ operands[1] = bfin_cc_rtx;
+})
+
+(define_expand "slt"
+ [(set (match_dup 1) (lt:BI (match_dup 2) (match_dup 3)))
+ (set (match_operand:SI 0 "register_operand" "")
+ (ne:SI (match_dup 1) (const_int 0)))]
+ ""
+{
+ operands[2] = bfin_compare_op0;
+ operands[3] = bfin_compare_op1;
+ operands[1] = bfin_cc_rtx;
+})
+
+(define_expand "sle"
+ [(set (match_dup 1) (le:BI (match_dup 2) (match_dup 3)))
+ (set (match_operand:SI 0 "register_operand" "")
+ (ne:SI (match_dup 1) (const_int 0)))]
+ ""
+{
+ operands[2] = bfin_compare_op0;
+ operands[3] = bfin_compare_op1;
+ operands[1] = bfin_cc_rtx;
+})
+
+(define_expand "sltu"
+ [(set (match_dup 1) (ltu:BI (match_dup 2) (match_dup 3)))
+ (set (match_operand:SI 0 "register_operand" "")
+ (ne:SI (match_dup 1) (const_int 0)))]
+ ""
+{
+ operands[2] = bfin_compare_op0;
+ operands[3] = bfin_compare_op1;
+ operands[1] = bfin_cc_rtx;
+})
+
+(define_expand "sleu"
+ [(set (match_dup 1) (leu:BI (match_dup 2) (match_dup 3)))
+ (set (match_operand:SI 0 "register_operand" "")
+ (ne:SI (match_dup 1) (const_int 0)))]
+ ""
+{
+ operands[2] = bfin_compare_op0;
+ operands[3] = bfin_compare_op1;
+ operands[1] = bfin_cc_rtx;
+})
+
+(define_insn "nop"
+ [(const_int 0)]
+ ""
+ "nop;")
+
+;;;;;;;;;;;;;;;;;;;; CC2dreg ;;;;;;;;;;;;;;;;;;;;;;;;;
+(define_insn "movsibi"
+ [(set (match_operand:BI 0 "cc_operand" "=C")
+ (ne:BI (match_operand:SI 1 "register_operand" "d")
+ (const_int 0)))]
+ ""
+ "CC = %1;"
+ [(set_attr "length" "2")])
+
+(define_insn "movbisi"
+ [(set (match_operand:SI 0 "register_operand" "=d")
+ (ne:SI (match_operand:BI 1 "cc_operand" "C")
+ (const_int 0)))]
+ ""
+ "%0 = CC;"
+ [(set_attr "length" "2")])
+
+(define_insn ""
+ [(set (match_operand:BI 0 "cc_operand" "=C")
+ (eq:BI (match_operand:BI 1 "cc_operand" " 0")
+ (const_int 0)))]
+ ""
+ "%0 = ! %0;" /* NOT CC;" */
+ [(set_attr "type" "compare")])
+
+;; Vector and DSP insns
+
+(define_insn ""
+ [(set (match_operand:SI 0 "register_operand" "=d")
+ (ior:SI (ashift:SI (match_operand:SI 1 "register_operand" "d")
+ (const_int 24))
+ (lshiftrt:SI (match_operand:SI 2 "register_operand" "d")
+ (const_int 8))))]
+ ""
+ "%0 = ALIGN8(%1, %2);"
+ [(set_attr "type" "dsp32")])
+
+(define_insn ""
+ [(set (match_operand:SI 0 "register_operand" "=d")
+ (ior:SI (ashift:SI (match_operand:SI 1 "register_operand" "d")
+ (const_int 16))
+ (lshiftrt:SI (match_operand:SI 2 "register_operand" "d")
+ (const_int 16))))]
+ ""
+ "%0 = ALIGN16(%1, %2);"
+ [(set_attr "type" "dsp32")])
+
+(define_insn ""
+ [(set (match_operand:SI 0 "register_operand" "=d")
+ (ior:SI (ashift:SI (match_operand:SI 1 "register_operand" "d")
+ (const_int 8))
+ (lshiftrt:SI (match_operand:SI 2 "register_operand" "d")
+ (const_int 24))))]
+ ""
+ "%0 = ALIGN24(%1, %2);"
+ [(set_attr "type" "dsp32")])
+
+;; Prologue and epilogue.
+
+(define_expand "prologue"
+ [(const_int 1)]
+ ""
+ "bfin_expand_prologue (); DONE;")
+
+(define_expand "epilogue"
+ [(const_int 1)]
+ ""
+ "bfin_expand_epilogue (1, 0); DONE;")
+
+(define_expand "sibcall_epilogue"
+ [(const_int 1)]
+ ""
+ "bfin_expand_epilogue (0, 0); DONE;")
+
+(define_expand "eh_return"
+ [(unspec_volatile [(match_operand:SI 0 "register_operand" "")]
+ UNSPEC_VOLATILE_EH_RETURN)]
+ ""
+{
+ emit_move_insn (EH_RETURN_HANDLER_RTX, operands[0]);
+ emit_insn (gen_eh_return_internal ());
+ emit_barrier ();
+})
+
+(define_insn_and_split "eh_return_internal"
+ [(unspec_volatile [(reg:SI REG_P2)] UNSPEC_VOLATILE_EH_RETURN)]
+ ""
+ "#"
+ "reload_completed"
+ [(const_int 1)]
+ "bfin_expand_epilogue (1, 1); DONE;")
+
+(define_insn "link"
+ [(set (mem:SI (plus:SI (reg:SI REG_SP) (const_int -4))) (reg:SI REG_RETS))
+ (set (mem:SI (plus:SI (reg:SI REG_SP) (const_int -8))) (reg:SI REG_FP))
+ (set (reg:SI REG_FP)
+ (plus:SI (reg:SI REG_SP) (const_int -8)))
+ (set (reg:SI REG_SP)
+ (plus:SI (reg:SI REG_SP) (match_operand:SI 0 "immediate_operand" "i")))]
+ ""
+ "LINK %Z0;"
+ [(set_attr "length" "4")])
+
+(define_insn "unlink"
+ [(set (reg:SI REG_FP) (mem:SI (reg:SI REG_FP)))
+ (set (reg:SI REG_RETS) (mem:SI (plus:SI (reg:SI REG_FP) (const_int 4))))
+ (set (reg:SI REG_SP) (plus:SI (reg:SI REG_FP) (const_int 8)))]
+ ""
+ "UNLINK;"
+ [(set_attr "length" "4")])
+
+;; This pattern is slightly clumsy. The stack adjust must be the final SET in
+;; the pattern, otherwise dwarf2out becomes very confused about which reg goes
+;; where on the stack, since it goes through all elements of the parallel in
+;; sequence.
+(define_insn "push_multiple"
+ [(match_parallel 0 "push_multiple_operation"
+ [(unspec [(match_operand:SI 1 "immediate_operand" "i")] UNSPEC_PUSH_MULTIPLE)])]
+ ""
+{
+ output_push_multiple (insn, operands);
+ return "";
+})
+
+(define_insn "pop_multiple"
+ [(match_parallel 0 "pop_multiple_operation"
+ [(set (reg:SI REG_SP)
+ (plus:SI (reg:SI REG_SP) (match_operand:SI 1 "immediate_operand" "i")))])]
+ ""
+{
+ output_pop_multiple (insn, operands);
+ return "";
+})
+
+(define_insn "return_internal"
+ [(return)
+ (unspec [(match_operand 0 "immediate_operand" "i")] UNSPEC_RETURN)]
+ "reload_completed"
+{
+ switch (INTVAL (operands[0]))
+ {
+ case EXCPT_HANDLER:
+ return "rtx;";
+ case NMI_HANDLER:
+ return "rtn;";
+ case INTERRUPT_HANDLER:
+ return "rti;";
+ case SUBROUTINE:
+ return "rts;";
+ }
+ gcc_unreachable ();
+})
+
+;;; Vector instructions
+
+(define_insn "addv2hi"
+ [(set (match_operand:V2HI 0 "register_operand" "=d")
+ (plus:V2HI (match_operand:V2HI 1 "register_operand" "d")
+ (match_operand:V2HI 2 "register_operand" "d")))]
+ ""
+ "%0 = %1 +|+ %2;"
+ [(set_attr "type" "dsp32")])
+
+(define_insn "subv2hi"
+ [(set (match_operand:V2HI 0 "register_operand" "=d")
+ (minus:V2HI (match_operand:V2HI 1 "register_operand" "d")
+ (match_operand:V2HI 2 "register_operand" "d")))]
+ ""
+ "%0 = %1 -|- %2;"
+ [(set_attr "type" "dsp32")])
+
+(define_insn "sminv2hi"
+ [(set (match_operand:V2HI 0 "register_operand" "=d")
+ (smin:V2HI (match_operand:V2HI 1 "register_operand" "d")
+ (match_operand:V2HI 2 "register_operand" "d")))]
+ ""
+ "%0 = MIN (%1, %2) (V);"
+ [(set_attr "type" "dsp32")])
+
+(define_insn "smaxv2hi"
+ [(set (match_operand:V2HI 0 "register_operand" "=d")
+ (smax:V2HI (match_operand:V2HI 1 "register_operand" "d")
+ (match_operand:V2HI 2 "register_operand" "d")))]
+ ""
+ "%0 = MAX (%1, %2) (V);"
+ [(set_attr "type" "dsp32")])
+
+(define_insn "mulv2hi"
+ [(set (match_operand:V2HI 0 "register_operand" "=d")
+ (mult:V2HI (match_operand:V2HI 1 "register_operand" "d")
+ (match_operand:V2HI 2 "register_operand" "d")))]
+ ""
+ "%h0 = %h1 * %h2, %d0 = %d1 * %d2 (IS);"
+ [(set_attr "type" "dsp32")])
+
+(define_insn "negv2hi"
+ [(set (match_operand:V2HI 0 "register_operand" "=d")
+ (neg:V2HI (match_operand:V2HI 1 "register_operand" "d")))]
+ ""
+ "%0 = - %1 (V);"
+ [(set_attr "type" "dsp32")])
+
+(define_insn "absv2hi"
+ [(set (match_operand:V2HI 0 "register_operand" "=d")
+ (abs:V2HI (match_operand:V2HI 1 "register_operand" "d")))]
+ ""
+ "%0 = ABS %1 (V);"
+ [(set_attr "type" "dsp32")])
+
--- /dev/null
+; Options for the Blackfin port of the compiler
+;
+; Copyright (C) 2005 Free Software Foundation, Inc.
+;
+; 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 2, 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 COPYING. If not, write to the Free
+; Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+; 02111-1307, USA.
+
+momit-leaf-frame-pointer
+Target Report Mask(OMIT_LEAF_FRAME_POINTER)
+Omit frame pointer for leaf functions
+
+mlow64k
+Target Report Mask(LOW_64K)
+Program is entirely located in low 64k of memory.
+
+mcsync
+Target Report Mask(CSYNC)
+Avoid speculative loads by inserting CSYNC or equivalent
+
+mid-shared-library
+Target Report Mask(ID_SHARED_LIBRARY)
+Enabled ID based shared library
--- /dev/null
+/* Specialized code needed to support construction and destruction of
+ file-scope objects in C++ and Java code, and to support exception handling.
+ Copyright (C) 2005 Free Software Foundation, Inc.
+ Contributed by Analog Devices.
+
+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 2, 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 COPYING. If not, write to
+the Free Software Foundation, 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA. */
+
+/* As a special exception, if you link this library with files
+ compiled with GCC to produce an executable, this does not cause
+ the resulting executable to be covered by the GNU General Public License.
+ This exception does not however invalidate any other reasons why
+ the executable file might be covered by the GNU General Public License. */
+
+/*
+ * This file just supplies function prologues for the .init and .fini
+ * sections. It is linked in before crtbegin.o.
+ */
+
+ .file "crti.o"
+ .ident "GNU C crti.o"
+
+ .section .init
+ .globl _init
+ .type _init,@function
+_init:
+ LINK 0;
+
+ .section .fini
+ .globl _fini
+ .type _fini,@function
+_fini:
+ LINK 0;
--- /dev/null
+/* Specialized code needed to support construction and destruction of
+ file-scope objects in C++ and Java code, and to support exception handling.
+ Copyright (C) 2005 Free Software Foundation, Inc.
+ Contributed by Analog Devices.
+
+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 2, 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 COPYING. If not, write to
+the Free Software Foundation, 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA. */
+
+/* As a special exception, if you link this library with files
+ compiled with GCC to produce an executable, this does not cause
+ the resulting executable to be covered by the GNU General Public License.
+ This exception does not however invalidate any other reasons why
+ the executable file might be covered by the GNU General Public License. */
+
+/*
+ * This file supplies function epilogues for the .init and .fini sections.
+ * It is linked in after all other files.
+ */
+
+ .file "crtn.o"
+ .ident "GNU C crtn.o"
+
+ .section .init
+ unlink;
+ rts;
+
+ .section .fini
+ unlink;
+ rts;
--- /dev/null
+#define OBJECT_FORMAT_ELF
+
+#define LOCAL_LABEL_PREFIX "L$"
+
+#undef ASM_GENERATE_INTERNAL_LABEL
+#define ASM_GENERATE_INTERNAL_LABEL(LABEL, PREFIX, NUM) \
+ sprintf (LABEL, "*%s%s$%d", LOCAL_LABEL_PREFIX, PREFIX, (int) NUM)
+
+#undef STARTFILE_SPEC
+#define STARTFILE_SPEC "crt0%O%s crti%O%s crtbegin%O%s"
+
+#undef ENDFILE_SPEC
+#define ENDFILE_SPEC "crtend%O%s crtn%O%s"
+
+#undef USER_LABEL_PREFIX
+#define USER_LABEL_PREFIX "_"
--- /dev/null
+/* libgcc functions for Blackfin.
+ Copyright (C) 2005 Free Software Foundation, Inc.
+ Contributed by Analog Devices.
+
+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 2, 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 COPYING. If not, write to
+the Free Software Foundation, 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA. */
+
+/* As a special exception, if you link this library with files
+ compiled with GCC to produce an executable, this does not cause
+ the resulting executable to be covered by the GNU General Public License.
+ This exception does not however invalidate any other reasons why
+ the executable file might be covered by the GNU General Public License. */
+
+
+#ifdef L_divsi3
+.text
+.align 2
+.global ___divsi3;
+.type ___divsi3, STT_FUNC;
+
+___divsi3:
+ [--SP]= RETS;
+ [--SP] = R7;
+
+ R2 = -R0;
+ CC = R0 < 0;
+ IF CC R0 = R2;
+ R7 = CC;
+
+ R2 = -R1;
+ CC = R1 < 0;
+ IF CC R1 = R2;
+ R2 = CC;
+ R7 = R7 ^ R2;
+
+ CALL ___udivsi3;
+
+ CC = R7;
+ R1 = -R0;
+ IF CC R0 = R1;
+
+ R7 = [SP++];
+ RETS = [SP++];
+ RTS;
+#endif
+
+#ifdef L_modsi3
+.align 2
+.global ___modsi3;
+.type ___modsi3, STT_FUNC;
+
+___modsi3:
+ [--SP] = RETS;
+ /* P1 and P2 are preserved by divsi3 and udivsi3. */
+ P1 = R0;
+ P2 = R1;
+ CALL ___divsi3;
+ R1 = P1;
+ R2 = P2;
+ R2 *= R0;
+ R0 = R1 - R2;
+ RETS = [SP++];
+ RTS;
+#endif
+
+#ifdef L_udivsi3
+.align 2
+.global ___udivsi3;
+.type ___udivsi3, STT_FUNC;
+
+___udivsi3:
+ P0 = 32;
+ LSETUP (0f, 1f) LC0 = P0;
+ /* upper half of dividend */
+ R3 = 0;
+0:
+ /* The first time round in the loop we shift in garbage, but since we
+ perform 33 shifts, it doesn't matter. */
+ R0 = ROT R0 BY 1;
+ R3 = ROT R3 BY 1;
+ R2 = R3 - R1;
+ CC = R3 < R1 (IU);
+1:
+ /* Last instruction of the loop. */
+ IF ! CC R3 = R2;
+
+ /* Shift in the last bit. */
+ R0 = ROT R0 BY 1;
+ /* R0 is the result, R3 contains the remainder. */
+ R0 = ~ R0;
+ RTS;
+#endif
+
+#ifdef L_umodsi3
+.align 2
+.global ___umodsi3;
+.type ___umodsi3, STT_FUNC;
+
+___umodsi3:
+ P1 = RETS;
+ CALL ___udivsi3;
+ R0 = R3;
+ RETS = P1;
+ RTS;
+#endif
+
--- /dev/null
+;; Predicate definitions for the Blackfin.
+;;
+;; 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 2, 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 COPYING. If not, write to
+;; the Free Software Foundation, 59 Temple Place - Suite 330,
+;; Boston, MA 02111-1307, USA.
+
+;; Return nonzero iff OP is one of the integer constants 1 or 2.
+(define_predicate "pos_scale_operand"
+ (and (match_code "const_int")
+ (match_test "INTVAL (op) == 1 || INTVAL (op) == 2")))
+
+;; Return nonzero iff OP is one of the integer constants 2 or 4.
+(define_predicate "scale_by_operand"
+ (and (match_code "const_int")
+ (match_test "INTVAL (op) == 2 || INTVAL (op) == 4")))
+
+;; Return nonzero if OP is a constant that consists of two parts; lower
+;; bits all zero and upper bits all ones. In this case, we can perform
+;; an AND operation with a sequence of two shifts. Don't return nonzero
+;; if the constant would be cheap to load.
+(define_predicate "highbits_operand"
+ (and (match_code "const_int")
+ (match_test "log2constp (-INTVAL (op)) && !CONST_7BIT_IMM_P (INTVAL (op))")))
+
+;; Return nonzero if OP is suitable as a right-hand side operand for an
+;; andsi3 operation.
+(define_predicate "rhs_andsi3_operand"
+ (ior (match_operand 0 "register_operand")
+ (and (match_code "const_int")
+ (match_test "log2constp (~INTVAL (op)) || INTVAL (op) == 255 || INTVAL (op) == 65535"))))
+
+;; Return nonzero if OP is a register or a constant with exactly one bit
+;; set.
+(define_predicate "regorlog2_operand"
+ (ior (match_operand 0 "register_operand")
+ (and (match_code "const_int")
+ (match_test "log2constp (INTVAL (op))"))))
+
+;; Like register_operand, but make sure that hard regs have a valid mode.
+(define_predicate "valid_reg_operand"
+ (match_operand 0 "register_operand")
+{
+ if (GET_CODE (op) == SUBREG)
+ op = SUBREG_REG (op);
+ if (REGNO (op) < FIRST_PSEUDO_REGISTER)
+ return HARD_REGNO_MODE_OK (REGNO (op), mode);
+ return 1;
+})
+
+;; Return nonzero if OP is the CC register.
+(define_predicate "cc_operand"
+ (and (match_code "reg")
+ (match_test "REGNO (op) == REG_CC && GET_MODE (op) == BImode")))
+
+;; Return nonzero if OP is a register or a 7 bit signed constant.
+(define_predicate "reg_or_7bit_operand"
+ (ior (match_operand 0 "register_operand")
+ (and (match_code "const_int")
+ (match_test "CONST_7BIT_IMM_P (INTVAL (op))"))))
+
+;; Used for secondary reloads, this function returns 1 if OP is of the
+;; form (plus (fp) (const_int)).
+(define_predicate "fp_plus_const_operand"
+ (match_code "plus")
+{
+ rtx op1, op2;
+
+ op1 = XEXP (op, 0);
+ op2 = XEXP (op, 1);
+ return (REG_P (op1)
+ && (REGNO (op1) == FRAME_POINTER_REGNUM
+ || REGNO (op1) == STACK_POINTER_REGNUM)
+ && GET_CODE (op2) == CONST_INT);
+})
+
+;; Returns 1 if OP is a symbolic operand, i.e. a symbol_ref or a label_ref,
+;; possibly with an offset.
+(define_predicate "symbolic_operand"
+ (ior (match_code "symbol_ref,label_ref")
+ (and (match_code "const")
+ (match_test "GET_CODE (XEXP (op,0)) == PLUS
+ && (GET_CODE (XEXP (XEXP (op, 0), 0)) == SYMBOL_REF
+ || GET_CODE (XEXP (XEXP (op, 0), 0)) == LABEL_REF)
+ && GET_CODE (XEXP (XEXP (op, 0), 1)) == CONST_INT"))))
+
+;; Returns 1 if OP is a plain constant or matched by symbolic_operand.
+(define_predicate "symbolic_or_const_operand"
+ (ior (match_code "const_int,const_double")
+ (match_operand 0 "symbolic_operand")))
+
+;; True for any non-virtual or eliminable register. Used in places where
+;; instantiation of such a register may cause the pattern to not be recognized.
+(define_predicate "register_no_elim_operand"
+ (match_operand 0 "register_operand")
+{
+ if (GET_CODE (op) == SUBREG)
+ op = SUBREG_REG (op);
+ return !(op == arg_pointer_rtx
+ || op == frame_pointer_rtx
+ || (REGNO (op) >= FIRST_PSEUDO_REGISTER
+ && REGNO (op) <= LAST_VIRTUAL_REGISTER));
+})
+
+;; Test for a valid operand for a call instruction. Don't allow the
+;; arg pointer register or virtual regs since they may decay into
+;; reg + const, which the patterns can't handle.
+;; We only allow SYMBOL_REF if !flag_pic.
+(define_predicate "call_insn_operand"
+ (ior (and (match_test "!flag_pic") (match_code "symbol_ref"))
+ (match_operand 0 "register_no_elim_operand")))
+
+;; Test for an operator valid in a conditional branch
+(define_predicate "bfin_cbranch_operator"
+ (match_code "eq,ne"))
--- /dev/null
+## Target part of the Makefile
+
+LIB1ASMSRC = bfin/lib1funcs.asm
+LIB1ASMFUNCS = _divsi3 _udivsi3 _umodsi3 _modsi3
+
+EXTRA_PARTS = crtbegin.o crtend.o crti.o crtn.o
+
+FPBIT = fp-bit.c
+DPBIT = dp-bit.c
+
+dp-bit.c: $(srcdir)/config/fp-bit.c
+ cat $(srcdir)/config/fp-bit.c > dp-bit.c
+
+fp-bit.c: $(srcdir)/config/fp-bit.c
+ echo '#define FLOAT' > fp-bit.c
+ cat $(srcdir)/config/fp-bit.c >> fp-bit.c
+
+# This shouldn't be needed here. I added it to the specs file for now, until
+# it is fixed in binutils (if it is necessary).
+GCC_CFLAGS += -N
+
+# Assemble startup files.
+$(T)crti.o: $(srcdir)/config/bfin/crti.s $(GCC_PASSES)
+ $(GCC_FOR_TARGET) $(GCC_CFLAGS) $(MULTILIB_CFLAGS) $(INCLUDES) \
+ -c -o $(T)crti.o -x assembler-with-cpp $(srcdir)/config/bfin/crti.s
+
+$(T)crtn.o: $(srcdir)/config/bfin/crtn.s $(GCC_PASSES)
+ $(GCC_FOR_TARGET) $(GCC_CFLAGS) $(MULTILIB_CFLAGS) $(INCLUDES) \
+ -c -o $(T)crtn.o -x assembler-with-cpp $(srcdir)/config/bfin/crtn.s
--- /dev/null
+## Target part of the Makefile
+
+LIB1ASMSRC = bfin/lib1funcs.asm
+LIB1ASMFUNCS = _divsi3 _udivsi3 _umodsi3 _modsi3
+
+EXTRA_PARTS = crtbegin.o crtend.o crti.o crtn.o
+
+FPBIT = fp-bit.c
+DPBIT = dp-bit.c
+
+dp-bit.c: $(srcdir)/config/fp-bit.c
+ cat $(srcdir)/config/fp-bit.c > dp-bit.c
+
+fp-bit.c: $(srcdir)/config/fp-bit.c
+ echo '#define FLOAT' > fp-bit.c
+ cat $(srcdir)/config/fp-bit.c >> fp-bit.c
+
+# This shouldn't be needed here. I added it to the specs file for now, until
+# it is fixed in binutils (if it is necessary).
+GCC_CFLAGS += -N
+
+# Assemble startup files.
+$(T)crti.o: $(srcdir)/config/bfin/crti.s $(GCC_PASSES)
+ $(GCC_FOR_TARGET) $(GCC_CFLAGS) $(MULTILIB_CFLAGS) $(INCLUDES) \
+ -c -o $(T)crti.o -x assembler-with-cpp $(srcdir)/config/bfin/crti.s
+
+$(T)crtn.o: $(srcdir)/config/bfin/crtn.s $(GCC_PASSES)
+ $(GCC_FOR_TARGET) $(GCC_CFLAGS) $(MULTILIB_CFLAGS) $(INCLUDES) \
+ -c -o $(T)crtn.o -x assembler-with-cpp $(srcdir)/config/bfin/crtn.s
You must use GAS and GLD from GNU binutils version 2.7 or later for
this attribute to work correctly.
+@item exception_handler
+@cindex exception handler functions on the Blackfin processor
+Use this attribute on the Blackfin to indicate that the specified function
+is an exception handler. The compiler will generate function entry and
+exit sequences suitable for use in an exception handler when this
+attribute is present.
+
@item far
@cindex functions which handle memory bank switching
On 68HC11 and 68HC12 the @code{far} attribute causes the compiler to
generate function entry and exit sequences suitable for use in an
interrupt handler when this attribute is present.
-Note, interrupt handlers for the m68k, H8/300, H8/300H, H8S, and SH processors
-can be specified via the @code{interrupt_handler} attribute.
+Note, interrupt handlers for the Blackfin, m68k, H8/300, H8/300H, H8S, and
+SH processors can be specified via the @code{interrupt_handler} attribute.
Note, on the AVR, interrupts will be enabled inside the function.
Permissible values for this parameter are: IRQ, FIQ, SWI, ABORT and UNDEF@.
@item interrupt_handler
-@cindex interrupt handler functions on the m68k, H8/300 and SH processors
-Use this attribute on the m68k, H8/300, H8/300H, H8S, and SH to indicate that
-the specified function is an interrupt handler. The compiler will generate
-function entry and exit sequences suitable for use in an interrupt
-handler when this attribute is present.
+@cindex interrupt handler functions on the Blackfin, m68k, H8/300 and SH processors
+Use this attribute on the Blackfin, m68k, H8/300, H8/300H, H8S, and SH to
+indicate that the specified function is an interrupt handler. The compiler
+will generate function entry and exit sequences suitable for use in an
+interrupt handler when this attribute is present.
+
+@item kspisusp
+@cindex User stack pointer in interrupts on the Blackfin
+When used together with @code{interrupt_handler}, @code{exception_handler}
+or @code{nmi_handler}, code will be generated to load the stack pointer
+from the USP register in the function prologue.
@item long_call/short_call
@cindex indirect calls on ARM
This attribute can be used to cancel the effect of the @option{-mlong-calls}
option.
+@item nesting
+@cindex Allow nesting in an interrupt handler on the Blackfin processor.
+Use this attribute together with @code{interrupt_handler},
+@code{exception_handler} or @code{nmi_handler} to indicate that the function
+entry code should enable nested interrupts or exceptions.
+
+@item nmi_handler
+@cindex NMI handler functions on the Blackfin processor
+Use this attribute on the Blackfin to indicate that the specified function
+is an NMI handler. The compiler will generate function entry and
+exit sequences suitable for use in an NMI handler when this
+attribute is present.
+
@item no_instrument_function
@cindex @code{no_instrument_function} function attribute
@opindex finstrument-functions
to be marked with the @code{noreturn} attribute.
@item saveall
-@cindex save all registers on the H8/300, H8/300H, and H8S
-Use this attribute on the H8/300, H8/300H, and H8S to indicate that
+@cindex save all registers on the Blackfin, H8/300, H8/300H, and H8S
+Use this attribute on the Blackfin, H8/300, H8/300H, and H8S to indicate that
all registers except the stack pointer should be saved in the prologue
regardless of whether they are used or not.
@item
@uref{#avr,,avr}
@item
+@uref{#bfin,,Blackfin}
+@item
@uref{#c4x,,c4x}
@item
@uref{#dos,,DOS}
@html
<hr />
@end html
+@heading @anchor{bfin}Blackfin
+
+The Blackfin processor, an Analog Devices DSP.
+@ifnothtml
+@xref{Blackfin Options,, Blackfin Options, gcc, Using and Porting the GNU
+Compiler Collection (GCC)},
+@end ifnothtml
+@ifhtml
+See ``Blackfin Options'' in the main manual
+@end ifhtml
+
+More information, and a version of binutils with support for this processor,
+is available at @uref{http://blackfin.uclinux.org}
+
+@html
+<hr />
+@end html
@heading @anchor{c4x}c4x
Texas Instruments TMS320C3x and TMS320C4x Floating Point Digital Signal
@gccoptlist{-mmcu=@var{mcu} -msize -minit-stack=@var{n} -mno-interrupts @gol
-mcall-prologues -mno-tablejump -mtiny-stack -mint8}
+@emph{Blackfin Options}
+@gccoptlist{-momit-leaf-frame-pointer -mno-omit-leaf-frame-pointer -mcsync @gol
+-mno-csync -mlow-64k -mno-low64k -mid-shared-library @gol
+-mno-id-shared-library -mshared-library-id=@var{n} @gol}
+
@emph{CRIS Options}
@gccoptlist{-mcpu=@var{cpu} -march=@var{cpu} -mtune=@var{cpu} @gol
-mmax-stack-frame=@var{n} -melinux-stacksize=@var{n} @gol
* ARC Options::
* ARM Options::
* AVR Options::
+* Blackfin Options::
* CRIS Options::
* Darwin Options::
* DEC Alpha Options::
size.
@end table
+@node Blackfin Options
+@subsection Blackfin Options
+@cindex Blackfin Options
+
+@table @gcctabopt
+@item -momit-leaf-frame-pointer
+@opindex momit-leaf-frame-pointer
+Don't keep the frame pointer in a register for leaf functions. This
+avoids the instructions to save, set up and restore frame pointers and
+makes an extra register available in leaf functions. The option
+@option{-fomit-frame-pointer} removes the frame pointer for all functions
+which might make debugging harder.
+
+@item -mcsync
+@opindex mcsync
+When enabled, the compiler will ensure that the generated code does not
+contain speculative loads after jump instructions. This option is enabled
+by default.
+
+@item -mno-csync
+@opindex mno-csync
+Don't generate extra code to prevent speculative loads from occurring.
+
+@item -mlow-64k
+@opindex
+When enabled, the compiler is free to take advantage of the knowledge that
+the entire program fits into the low 64k of memory.
+
+@item -mno-low-64k
+@opindex mno-low-64k
+Assume that the program is arbitrarily large. This is the default.
+
+@item -mid-shared-library
+@opindex mid-shared-library
+Generate code that supports shared libraries via the library ID method.
+This allows for execute in place and shared libraries in an environment
+without virtual memory management. This option implies @option{-fPIC}.
+
+@item -mno-id-shared-library
+@opindex mno-id-shared-library
+Generate code that doesn't assume ID based shared libraries are being used.
+This is the default.
+
+@item -mshared-library-id=n
+@opindex mshared-library-id
+Specified the identification number of the ID based shared library being
+compiled. Specifying a value of 0 will generate more compact code, specifying
+other values will force the allocation of that number to the current
+library but is no more space or time efficient than omitting this option.
+@end table
+
@node CRIS Options
@subsection CRIS Options
@cindex CRIS Options
@end table
+@item Blackfin family---@file{bfin.h}
+@table @code
+@item a
+P register
+
+@item d
+D register
+
+@item z
+A call clobbered P register.
+
+@item D
+Even-numbered D register
+
+@item W
+Odd-numbered D register
+
+@item e
+Accumulator register.
+
+@item A
+Even-numbered accumulator register.
+
+@item B
+Odd-numbered accumulator register.
+
+@item b
+I register
+
+@item B
+B register
+
+@item f
+M register
+
+@item c
+Registers used for circular buffering, i.e. I, B, or L registers.
+
+@item C
+The CC register.
+
+@item x
+Any D, P, B, M, I or L register.
+
+@item y
+Additional registers typically used only in prologues and epilogues: RETS,
+RETN, RETI, RETX, RETE, ASTAT, SEQSTAT and USP.
+
+@item w
+Any register except accumulators or CC.
+
+@item Ksh
+Signed 16 bit integer (in the range -32768 to 32767)
+
+@item Kuh
+Unsigned 16 bit integer (in the range 0 to 65535)
+
+@item Ks7
+Signed 7 bit integer (in the range -64 to 63)
+
+@item Ku7
+Unsigned 7 bit integer (in the range 0 to 127)
+
+@item Ku5
+Unsigned 5 bit integer (in the range 0 to 31)
+
+@item Ks4
+Signed 4 bit integer (in the range -8 to 7)
+
+@item Ks3
+Signed 3 bit integer (in the range -3 to 4)
+
+@item Ku3
+Unsigned 3 bit integer (in the range 0 to 7)
+
+@item P@var{n}
+Constant @var{n}, where @var{n} is a single-digit constant in the range 0 to 4.
+
+@item M1
+Constant 255.
+
+@item M2
+Constant 65535.
+
+@item J
+An integer constant with exactly a single bit set.
+
+@item L
+An integer constant with all bits set except exactly one.
+
+@item H
+
+@item Q
+Any SYMBOL_REF.
+@end table
+
@item IP2K---@file{ip2k.h}
@table @code
@item a