summaryrefslogtreecommitdiff
path: root/gnu/egcs/gcc/config/c4x
diff options
context:
space:
mode:
authorMarc Espie <espie@cvs.openbsd.org>1999-05-26 13:38:57 +0000
committerMarc Espie <espie@cvs.openbsd.org>1999-05-26 13:38:57 +0000
commit0126e157b87f137fc08dc7f46f6c291b9d06ac5d (patch)
treef8555e3e504eb82b4cd3cba5cec20ae4ce8124ff /gnu/egcs/gcc/config/c4x
parentff8e9a4356e55ed142306c3a375fa280800abc86 (diff)
egcs projects compiler system
Exact copy of the snapshot, except for the removal of texinfo/ gcc/ch/ libchill/
Diffstat (limited to 'gnu/egcs/gcc/config/c4x')
-rw-r--r--gnu/egcs/gcc/config/c4x/c4x.c4475
-rw-r--r--gnu/egcs/gcc/config/c4x/c4x.h2800
-rw-r--r--gnu/egcs/gcc/config/c4x/c4x.md7187
-rw-r--r--gnu/egcs/gcc/config/c4x/libgcc.S1501
-rw-r--r--gnu/egcs/gcc/config/c4x/t-c4x20
-rw-r--r--gnu/egcs/gcc/config/c4x/xm-c4x.h21
6 files changed, 16004 insertions, 0 deletions
diff --git a/gnu/egcs/gcc/config/c4x/c4x.c b/gnu/egcs/gcc/config/c4x/c4x.c
new file mode 100644
index 00000000000..5c5690a4eaf
--- /dev/null
+++ b/gnu/egcs/gcc/config/c4x/c4x.c
@@ -0,0 +1,4475 @@
+/* Subroutines for assembler code output on the TMS320C[34]x
+ Copyright (C) 1994-98, 1999 Free Software Foundation, Inc.
+
+ Contributed by Michael Hayes (m.hayes@elec.canterbury.ac.nz)
+ and Herman Ten Brugge (Haj.Ten.Brugge@net.HCC.nl).
+
+ 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. */
+
+/* Some output-actions in c4x.md need these. */
+#include "config.h"
+#include "system.h"
+#include "toplev.h"
+#include "rtl.h"
+#include "regs.h"
+#include "hard-reg-set.h"
+#include "basic-block.h"
+#include "real.h"
+#include "insn-config.h"
+#include "insn-attr.h"
+#include "insn-codes.h"
+#include "conditions.h"
+#include "insn-flags.h"
+#include "output.h"
+#include "tree.h"
+#include "expr.h"
+#include "flags.h"
+#include "loop.h"
+#include "recog.h"
+#include "c-tree.h"
+
+static int c4x_leaf_function;
+
+static char *float_reg_names[] = FLOAT_REGISTER_NAMES;
+
+/* Array of the smallest class containing reg number REGNO, indexed by
+ REGNO. Used by REGNO_REG_CLASS in c4x.h. We assume that all these
+ registers are available and set the class to NO_REGS for registers
+ that the target switches say are unavailable. */
+
+enum reg_class c4x_regclass_map[FIRST_PSEUDO_REGISTER] =
+{
+ /* Reg Modes Saved */
+ R0R1_REGS, /* R0 QI, QF, HF No */
+ R0R1_REGS, /* R1 QI, QF, HF No */
+ R2R3_REGS, /* R2 QI, QF, HF No */
+ R2R3_REGS, /* R3 QI, QF, HF No */
+ EXT_LOW_REGS, /* R4 QI, QF, HF QI */
+ EXT_LOW_REGS, /* R5 QI, QF, HF QI */
+ EXT_LOW_REGS, /* R6 QI, QF, HF QF */
+ EXT_LOW_REGS, /* R7 QI, QF, HF QF */
+ ADDR_REGS, /* AR0 QI No */
+ ADDR_REGS, /* AR1 QI No */
+ ADDR_REGS, /* AR2 QI No */
+ ADDR_REGS, /* AR3 QI QI */
+ ADDR_REGS, /* AR4 QI QI */
+ ADDR_REGS, /* AR5 QI QI */
+ ADDR_REGS, /* AR6 QI QI */
+ ADDR_REGS, /* AR7 QI QI */
+ DP_REG, /* DP QI No */
+ INDEX_REGS, /* IR0 QI No */
+ INDEX_REGS, /* IR1 QI No */
+ BK_REG, /* BK QI QI */
+ SP_REG, /* SP QI No */
+ ST_REG, /* ST CC No */
+ NO_REGS, /* DIE/IE No */
+ NO_REGS, /* IIE/IF No */
+ NO_REGS, /* IIF/IOF No */
+ INT_REGS, /* RS QI No */
+ INT_REGS, /* RE QI No */
+ RC_REG, /* RC QI No */
+ EXT_REGS, /* R8 QI, QF, HF QI */
+ EXT_REGS, /* R9 QI, QF, HF No */
+ EXT_REGS, /* R10 QI, QF, HF No */
+ EXT_REGS, /* R11 QI, QF, HF No */
+};
+
+enum machine_mode c4x_caller_save_map[FIRST_PSEUDO_REGISTER] =
+{
+ /* Reg Modes Saved */
+ HFmode, /* R0 QI, QF, HF No */
+ HFmode, /* R1 QI, QF, HF No */
+ HFmode, /* R2 QI, QF, HF No */
+ HFmode, /* R3 QI, QF, HF No */
+ QFmode, /* R4 QI, QF, HF QI */
+ QFmode, /* R5 QI, QF, HF QI */
+ QImode, /* R6 QI, QF, HF QF */
+ QImode, /* R7 QI, QF, HF QF */
+ QImode, /* AR0 QI No */
+ QImode, /* AR1 QI No */
+ QImode, /* AR2 QI No */
+ QImode, /* AR3 QI QI */
+ QImode, /* AR4 QI QI */
+ QImode, /* AR5 QI QI */
+ QImode, /* AR6 QI QI */
+ QImode, /* AR7 QI QI */
+ VOIDmode, /* DP QI No */
+ QImode, /* IR0 QI No */
+ QImode, /* IR1 QI No */
+ QImode, /* BK QI QI */
+ VOIDmode, /* SP QI No */
+ VOIDmode, /* ST CC No */
+ VOIDmode, /* DIE/IE No */
+ VOIDmode, /* IIE/IF No */
+ VOIDmode, /* IIF/IOF No */
+ QImode, /* RS QI No */
+ QImode, /* RE QI No */
+ VOIDmode, /* RC QI No */
+ QFmode, /* R8 QI, QF, HF QI */
+ HFmode, /* R9 QI, QF, HF No */
+ HFmode, /* R10 QI, QF, HF No */
+ HFmode, /* R11 QI, QF, HF No */
+};
+
+
+/* Test and compare insns in c4x.md store the information needed to
+ generate branch and scc insns here. */
+
+struct rtx_def *c4x_compare_op0 = NULL_RTX;
+struct rtx_def *c4x_compare_op1 = NULL_RTX;
+
+char *c4x_rpts_cycles_string;
+int c4x_rpts_cycles = 0; /* Max. cycles for RPTS */
+char *c4x_cpu_version_string;
+int c4x_cpu_version = 40; /* CPU version C30/31/32/40/44 */
+
+/* Pragma definitions. */
+
+tree code_tree = NULL_TREE;
+tree data_tree = NULL_TREE;
+tree pure_tree = NULL_TREE;
+tree noreturn_tree = NULL_TREE;
+tree interrupt_tree = NULL_TREE;
+
+
+/* Override command line options.
+ Called once after all options have been parsed.
+ Mostly we process the processor
+ type and sometimes adjust other TARGET_ options. */
+
+void
+c4x_override_options ()
+{
+ if (c4x_rpts_cycles_string)
+ c4x_rpts_cycles = atoi (c4x_rpts_cycles_string);
+ else
+ c4x_rpts_cycles = 0;
+
+ if (TARGET_C30)
+ c4x_cpu_version = 30;
+ else if (TARGET_C31)
+ c4x_cpu_version = 31;
+ else if (TARGET_C32)
+ c4x_cpu_version = 32;
+ else if (TARGET_C40)
+ c4x_cpu_version = 40;
+ else if (TARGET_C44)
+ c4x_cpu_version = 44;
+ else
+ c4x_cpu_version = 40;
+
+ /* -mcpu=xx overrides -m40 etc. */
+ if (c4x_cpu_version_string)
+ c4x_cpu_version = atoi (c4x_cpu_version_string);
+
+ target_flags &= ~(C30_FLAG | C31_FLAG | C32_FLAG | C40_FLAG | C44_FLAG);
+
+ switch (c4x_cpu_version)
+ {
+ case 30: target_flags |= C30_FLAG; break;
+ case 31: target_flags |= C31_FLAG; break;
+ case 32: target_flags |= C32_FLAG; break;
+ case 40: target_flags |= C40_FLAG; break;
+ case 44: target_flags |= C44_FLAG; break;
+ default:
+ warning ("Unknown CPU version %d, using 40.\n", c4x_cpu_version);
+ c4x_cpu_version = 40;
+ target_flags |= C40_FLAG;
+ }
+
+ if (TARGET_C30 || TARGET_C31 || TARGET_C32)
+ target_flags |= C3X_FLAG;
+ else
+ target_flags &= ~C3X_FLAG;
+
+ /* Convert foo / 8.0 into foo * 0.125, etc. */
+ flag_fast_math = 1;
+
+ /* We should phase out the following at some stage.
+ This provides compatibility with the old -mno-aliases option. */
+ if (! TARGET_ALIASES && ! flag_argument_noalias)
+ flag_argument_noalias = 1;
+}
+
+/* This is called before c4x_override_options. */
+void
+c4x_optimization_options (level, size)
+ int level;
+ int size ATTRIBUTE_UNUSED;
+{
+ /* Scheduling before register allocation can screw up global
+ register allocation, especially for functions that use MPY||ADD
+ instructions. The benefit we gain we get by scheduling before
+ register allocation is probably marginal anyhow. */
+ flag_schedule_insns = 0;
+
+ /* When optimizing, enable use of RPTB instruction. */
+ if (level >= 1)
+ flag_branch_on_count_reg = 1;
+}
+
+/* Write an ASCII string. */
+
+#define C4X_ASCII_LIMIT 40
+
+void
+c4x_output_ascii (stream, ptr, len)
+ FILE *stream;
+ unsigned char *ptr;
+ int len;
+{
+ char sbuf[C4X_ASCII_LIMIT + 1];
+ int s, first, onlys;
+
+ if (len)
+ {
+ fprintf (stream, "\t.byte\t");
+ first = 1;
+ }
+
+ for (s = 0; len > 0; --len, ++ptr)
+ {
+ onlys = 0;
+
+ /* Escape " and \ with a \". */
+ if (*ptr == '\"' || *ptr == '\\')
+ sbuf[s++] = '\\';
+
+ /* If printable - add to buff. */
+ if (*ptr >= 0x20 && *ptr < 0x7f)
+ {
+ sbuf[s++] = *ptr;
+ if (s < C4X_ASCII_LIMIT - 1)
+ continue;
+ onlys = 1;
+ }
+ if (s)
+ {
+ if (first)
+ first = 0;
+ else
+ fputc (',', stream);
+
+ sbuf[s] = 0;
+ fprintf (stream, "\"%s\"", sbuf);
+ s = 0;
+ }
+ if (onlys)
+ continue;
+
+ if (first)
+ first = 0;
+ else
+ fputc (',', stream);
+
+ fprintf (stream, "%d", *ptr);
+ }
+ if (s)
+ {
+ if (! first)
+ fputc (',', stream);
+
+ sbuf[s] = 0;
+ fprintf (stream, "\"%s\"", sbuf);
+ s = 0;
+ }
+ fputc ('\n', stream);
+}
+
+
+int
+c4x_hard_regno_mode_ok (regno, mode)
+ int regno;
+ enum machine_mode mode;
+{
+ switch (mode)
+ {
+#if Pmode != QImode
+ case Pmode: /* Pointer (24/32 bits) */
+#endif
+ case QImode: /* Integer (32 bits) */
+ return IS_INT_REG (regno);
+
+ case QFmode: /* Float, Double (32 bits) */
+ case HFmode: /* Long Double (40 bits) */
+ return IS_EXT_REG (regno);
+
+ case CCmode: /* Condition Codes */
+ case CC_NOOVmode: /* Condition Codes */
+ return IS_ST_REG (regno);
+
+ case HImode: /* Long Long (64 bits) */
+ /* We need two registers to store long longs. Note that
+ it is much easier to constrain the first register
+ to start on an even boundary. */
+ return IS_INT_REG (regno)
+ && IS_INT_REG (regno + 1)
+ && (regno & 1) == 0;
+
+ default:
+ return 0; /* We don't support these modes */
+ }
+
+ return 0;
+}
+
+
+/* The TI C3x C compiler register argument runtime model uses 6 registers,
+ AR2, R2, R3, RC, RS, RE.
+
+ The first two floating point arguments (float, double, long double)
+ that are found scanning from left to right are assigned to R2 and R3.
+
+ The remaining integer (char, short, int, long) or pointer arguments
+ are assigned to the remaining registers in the order AR2, R2, R3,
+ RC, RS, RE when scanning left to right, except for the last named
+ argument prior to an ellipsis denoting variable number of
+ arguments. We don't have to worry about the latter condition since
+ function.c treats the last named argument as anonymous (unnamed).
+
+ All arguments that cannot be passed in registers are pushed onto
+ the stack in reverse order (right to left). GCC handles that for us.
+
+ c4x_init_cumulative_args() is called at the start, so we can parse
+ the args to see how many floating point arguments and how many
+ integer (or pointer) arguments there are. c4x_function_arg() is
+ then called (sometimes repeatedly) for each argument (parsed left
+ to right) to obtain the register to pass the argument in, or zero
+ if the argument is to be passed on the stack. Once the compiler is
+ happy, c4x_function_arg_advance() is called.
+
+ Don't use R0 to pass arguments in, we use 0 to indicate a stack
+ argument. */
+
+static int c4x_int_reglist[3][6] =
+{
+ {AR2_REGNO, R2_REGNO, R3_REGNO, RC_REGNO, RS_REGNO, RE_REGNO},
+ {AR2_REGNO, R3_REGNO, RC_REGNO, RS_REGNO, RE_REGNO, 0},
+ {AR2_REGNO, RC_REGNO, RS_REGNO, RE_REGNO, 0, 0}
+};
+
+static int c4x_fp_reglist[2] = {R2_REGNO, R3_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. */
+
+void
+c4x_init_cumulative_args (cum, fntype, libname)
+ CUMULATIVE_ARGS *cum; /* argument info to initialize */
+ tree fntype; /* tree ptr for function decl */
+ rtx libname; /* SYMBOL_REF of library name or 0 */
+{
+ tree param, next_param;
+
+ cum->floats = cum->ints = 0;
+ cum->init = 0;
+ cum->var = 0;
+ cum->args = 0;
+
+ if (TARGET_DEBUG)
+ {
+ fprintf (stderr, "\nc4x_init_cumulative_args (");
+ if (fntype)
+ {
+ tree ret_type = TREE_TYPE (fntype);
+
+ fprintf (stderr, "fntype code = %s, ret code = %s",
+ tree_code_name[(int) TREE_CODE (fntype)],
+ tree_code_name[(int) TREE_CODE (ret_type)]);
+ }
+ else
+ fprintf (stderr, "no fntype");
+
+ if (libname)
+ fprintf (stderr, ", libname = %s", XSTR (libname, 0));
+ }
+
+ cum->prototype = (fntype && TYPE_ARG_TYPES (fntype));
+
+ for (param = fntype ? TYPE_ARG_TYPES (fntype) : 0;
+ param; param = next_param)
+ {
+ tree type;
+
+ next_param = TREE_CHAIN (param);
+
+ type = TREE_VALUE (param);
+ if (type && type != void_type_node)
+ {
+ enum machine_mode mode;
+
+ /* If the last arg doesn't have void type then we have
+ variable arguments. */
+ if (! next_param)
+ cum->var = 1;
+
+ if ((mode = TYPE_MODE (type)))
+ {
+ if (! MUST_PASS_IN_STACK (mode, type))
+ {
+ /* Look for float, double, or long double argument. */
+ if (mode == QFmode || mode == HFmode)
+ cum->floats++;
+ /* Look for integer, enumeral, boolean, char, or pointer
+ argument. */
+ else if (mode == QImode || mode == Pmode)
+ cum->ints++;
+ }
+ }
+ cum->args++;
+ }
+ }
+
+ if (TARGET_DEBUG)
+ fprintf (stderr, "%s%s, args = %d)\n",
+ cum->prototype ? ", prototype" : "",
+ cum->var ? ", variable args" : "",
+ cum->args);
+}
+
+
+/* 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
+c4x_function_arg_advance (cum, mode, type, named)
+ CUMULATIVE_ARGS *cum; /* current arg information */
+ enum machine_mode mode; /* current arg mode */
+ tree type; /* type of the argument or 0 if lib support */
+ int named; /* whether or not the argument was named */
+{
+ if (TARGET_DEBUG)
+ fprintf (stderr, "c4x_function_adv(mode=%s, named=%d)\n\n",
+ GET_MODE_NAME (mode), named);
+ if (! TARGET_MEMPARM
+ && named
+ && type
+ && ! MUST_PASS_IN_STACK (mode, type))
+ {
+ /* Look for float, double, or long double argument. */
+ if (mode == QFmode || mode == HFmode)
+ cum->floats++;
+ /* Look for integer, enumeral, boolean, char, or pointer argument. */
+ else if (mode == QImode || mode == Pmode)
+ cum->ints++;
+ }
+ else if (! TARGET_MEMPARM && ! type)
+ {
+ /* Handle libcall arguments. */
+ if (mode == QFmode || mode == HFmode)
+ cum->floats++;
+ else if (mode == QImode || mode == Pmode)
+ cum->ints++;
+ }
+ 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 *
+c4x_function_arg (cum, mode, type, named)
+ CUMULATIVE_ARGS *cum; /* current arg information */
+ enum machine_mode mode; /* current arg mode */
+ tree type; /* type of the argument or 0 if lib support */
+ int named; /* != 0 for normal args, == 0 for ... args */
+{
+ int reg = 0; /* default to passing argument on stack */
+
+ if (! cum->init)
+ {
+ /* We can handle at most 2 floats in R2, R3 */
+ cum->maxfloats = (cum->floats > 2) ? 2 : cum->floats;
+
+ /* We can handle at most 6 integers minus number of floats passed
+ in registers. */
+ cum->maxints = (cum->ints > 6 - cum->maxfloats) ?
+ 6 - cum->maxfloats : cum->ints;
+
+ /* If there is no prototype, assume all the arguments are integers. */
+ if (! cum->prototype)
+ cum->maxints = 6;
+
+ cum->ints = cum->floats = 0;
+ cum->init = 1;
+ }
+
+ if (! TARGET_MEMPARM
+ && named
+ && type
+ && ! MUST_PASS_IN_STACK (mode, type))
+ {
+ /* Look for float, double, or long double argument. */
+ if (mode == QFmode || mode == HFmode)
+ {
+ if (cum->floats < cum->maxfloats)
+ reg = c4x_fp_reglist[cum->floats];
+ }
+ /* Look for integer, enumeral, boolean, char, or pointer argument. */
+ else if (mode == QImode || mode == Pmode)
+ {
+ if (cum->ints < cum->maxints)
+ reg = c4x_int_reglist[cum->maxfloats][cum->ints];
+ }
+ }
+ else if (! TARGET_MEMPARM && ! type)
+ {
+ /* We could use a different argument calling model for libcalls,
+ since we're only calling functions in libgcc. Thus we could
+ pass arguments for long longs in registers rather than on the
+ stack. In the meantime, use the odd TI format. We make the
+ assumption that we won't have more than two floating point
+ args, six integer args, and that all the arguments are of the
+ same mode. */
+ if (mode == QFmode || mode == HFmode)
+ reg = c4x_fp_reglist[cum->floats];
+ else if (mode == QImode || mode == Pmode)
+ reg = c4x_int_reglist[0][cum->ints];
+ }
+
+ if (TARGET_DEBUG)
+ {
+ fprintf (stderr, "c4x_function_arg(mode=%s, named=%d",
+ GET_MODE_NAME (mode), named);
+ if (reg)
+ fprintf (stderr, ", reg=%s", reg_names[reg]);
+ else
+ fprintf (stderr, ", stack");
+ fprintf (stderr, ")\n");
+ }
+ if (reg)
+ return gen_rtx_REG (mode, reg);
+ else
+ return NULL_RTX;
+}
+
+
+static int
+c4x_isr_reg_used_p (regno)
+ int regno;
+{
+ /* Don't save/restore FP or ST, we handle them separately. */
+ if (regno == FRAME_POINTER_REGNUM
+ || IS_ST_REG (regno))
+ return 0;
+
+ /* We could be a little smarter abut saving/restoring DP.
+ We'll only save if for the big memory model or if
+ we're paranoid. ;-) */
+ if (IS_DP_REG (regno))
+ return ! TARGET_SMALL || TARGET_PARANOID;
+
+ /* Only save/restore regs in leaf function that are used. */
+ if (c4x_leaf_function)
+ return regs_ever_live[regno] && fixed_regs[regno] == 0;
+
+ /* Only save/restore regs that are used by the ISR and regs
+ that are likely to be used by functions the ISR calls
+ if they are not fixed. */
+ return IS_EXT_REG (regno)
+ || ((regs_ever_live[regno] || call_used_regs[regno])
+ && fixed_regs[regno] == 0);
+}
+
+
+static int
+c4x_leaf_function_p ()
+{
+ /* A leaf function makes no calls, so we only need
+ to save/restore the registers we actually use.
+ For the global variable leaf_function to be set, we need
+ to define LEAF_REGISTERS and all that it entails.
+ Let's check ourselves... */
+
+ if (lookup_attribute ("leaf_pretend",
+ TYPE_ATTRIBUTES (TREE_TYPE (current_function_decl))))
+ return 1;
+
+ /* Use the leaf_pretend attribute at your own risk. This is a hack
+ to speed up ISRs that call a function infrequently where the
+ overhead of saving and restoring the additional registers is not
+ warranted. You must save and restore the additional registers
+ required by the called function. Caveat emptor. Here's enough
+ rope... */
+
+ if (leaf_function_p ())
+ return 1;
+
+ return 0;
+}
+
+
+static int
+c4x_assembler_function_p ()
+{
+ tree type;
+
+ type = TREE_TYPE (current_function_decl);
+ return lookup_attribute ("assembler", TYPE_ATTRIBUTES (type)) != NULL;
+}
+
+
+static int
+c4x_interrupt_function_p ()
+{
+ if (lookup_attribute ("interrupt",
+ TYPE_ATTRIBUTES (TREE_TYPE (current_function_decl))))
+ return 1;
+
+ /* Look for TI style c_intnn */
+ return current_function_name[0] == 'c'
+ && current_function_name[1] == '_'
+ && current_function_name[2] == 'i'
+ && current_function_name[3] == 'n'
+ && current_function_name[4] == 't'
+ && isdigit (current_function_name[5])
+ && isdigit (current_function_name[6]);
+}
+
+
+/* Write function prologue. */
+
+void
+c4x_function_prologue (file, size)
+ FILE *file;
+ int size;
+{
+ int regno;
+
+/* In functions where ar3 is not used but frame pointers are still
+ specified, frame pointers are not adjusted (if >= -O2) and this is
+ used so it won't be needlessly push the frame pointer. */
+ int dont_push_ar3;
+
+ /* For __assembler__ function don't build a prologue. */
+ if (c4x_assembler_function_p ())
+ {
+ fprintf (file, "; *** Assembler Function ***\n");
+ return;
+ }
+
+ /* For __interrupt__ function build specific prologue. */
+ if (c4x_interrupt_function_p ())
+ {
+ c4x_leaf_function = c4x_leaf_function_p ();
+ fprintf (file, "; *** Interrupt Entry %s ***\n",
+ c4x_leaf_function ? "(leaf)" : "");
+
+ fprintf (file, "\tpush\tst\n");
+ if (size)
+ {
+ fprintf (file, "\tpush\tar3\n\tldi\tsp,ar3\n");
+ /* FIXME: Assume ISR doesn't require more than 32767 words
+ of local variables. */
+ if (size > 32767)
+ error ("ISR %s requires %d words of local variables, "
+ "maximum is 32767.", current_function_name, size);
+ fprintf (file, "\taddi\t%d,sp\n", size);
+ }
+ for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
+ {
+ if (c4x_isr_reg_used_p (regno))
+ {
+ fprintf (file, "\tpush\t%s\n", reg_names[regno]);
+ if (IS_EXT_REG (regno)) /* save 32MSB of R0--R11 */
+ fprintf (file, "\tpushf\t%s\n", float_reg_names[regno]);
+ }
+ }
+ /* We need to clear the repeat mode flag if the ISR is
+ going to use a RPTB instruction or uses the RC, RS, or RE
+ registers. */
+ if (regs_ever_live[RC_REGNO]
+ || regs_ever_live[RS_REGNO]
+ || regs_ever_live[RE_REGNO])
+ fprintf (file, "\tandn\t0100h,st\n");
+
+ /* Reload DP reg if we are paranoid about some turkey
+ violating small memory model rules. */
+ if (TARGET_SMALL && TARGET_PARANOID)
+ fprintf (file, TARGET_C3X ?
+ "\tldp\t@data_sec\n" :
+ "\tldpk\t@data_sec\n");
+ }
+ else
+ {
+ if (frame_pointer_needed)
+ {
+ if ((size != 0)
+ || (current_function_args_size != 0)
+ || (optimize < 2))
+ {
+ fprintf (file, "\tpush\tar3\n");
+ fprintf (file, "\tldi\tsp,ar3\n");
+ dont_push_ar3 = 1;
+ }
+ else
+ {
+ /* Since ar3 is not used, we don't need to push it. */
+ dont_push_ar3 = 1;
+ }
+ }
+ else
+ {
+ /* If we use ar3, we need to push it. */
+ dont_push_ar3 = 0;
+ if ((size != 0) || (current_function_args_size != 0))
+ {
+ /* If we are omitting the frame pointer, we still have
+ to make space for it so the offsets are correct
+ unless we don't use anything on the stack at all. */
+ size += 1;
+ }
+ }
+
+ if (size > 32767)
+ {
+ /* Local vars are too big, it will take multiple operations
+ to increment SP. */
+ if (TARGET_C3X)
+ {
+ fprintf (file, "\tldi\t%d,r1\n", size >> 16);
+ fprintf (file, "\tlsh\t16,r1\n");
+ }
+ else
+ fprintf (file, "\tldhi\t%d,r1\n", size >> 16);
+ fprintf (file, "\tor\t%d,r1\n", size & 0xffff);
+ fprintf (file, "\taddi\tr1,sp\n");
+ }
+ else if (size != 0)
+ {
+ /* Local vars take up less than 32767 words, so we can directly
+ add the number. */
+ fprintf (file, "\taddi\t%d,sp\n", size);
+ }
+
+ for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
+ {
+ if (regs_ever_live[regno] && ! call_used_regs[regno])
+ {
+ if ((regno == R6_REGNO) || (regno == R7_REGNO))
+ {
+ /* R6 and R7 are saved as floating point */
+ if (TARGET_PRESERVE_FLOAT)
+ fprintf (file, "\tpush\t%s\n", reg_names[regno]);
+ fprintf (file, "\tpushf\t%s\n", float_reg_names[regno]);
+ }
+ else if ((! dont_push_ar3) || (regno != AR3_REGNO))
+ {
+ fprintf (file, "\tpush\t%s\n", reg_names[regno]);
+ }
+ }
+ }
+ }
+}
+
+
+/* Write function epilogue. */
+
+void
+c4x_function_epilogue (file, size)
+ FILE *file;
+ int size;
+{
+ int regno;
+ int restore_count = 0;
+ int delayed_jump = 0;
+ int dont_pop_ar3;
+ rtx insn;
+
+ insn = get_last_insn ();
+ if (insn && GET_CODE (insn) == NOTE)
+ insn = prev_nonnote_insn (insn);
+
+ if (insn && GET_CODE (insn) == BARRIER)
+ return;
+
+ /* For __assembler__ function build no epilogue. */
+ if (c4x_assembler_function_p ())
+ {
+ fprintf (file, "\trets\n"); /* Play it safe */
+ return;
+ }
+
+#ifdef FUNCTION_BLOCK_PROFILER_EXIT
+ if (profile_block_flag == 2)
+ {
+ FUNCTION_BLOCK_PROFILER_EXIT (file);
+ }
+#endif
+
+ /* For __interrupt__ function build specific epilogue. */
+ if (c4x_interrupt_function_p ())
+ {
+ for (regno = FIRST_PSEUDO_REGISTER - 1; regno >= 0; --regno)
+ {
+ if (! c4x_isr_reg_used_p (regno))
+ continue;
+ if (IS_EXT_REG (regno))
+ fprintf (file, "\tpopf\t%s\n", float_reg_names[regno]);
+ fprintf (file, "\tpop\t%s\n", reg_names[regno]);
+ }
+ if (size)
+ {
+ fprintf (file, "\tsubi\t%d,sp\n", size);
+ fprintf (file, "\tpop\tar3\n");
+ }
+ fprintf (file, "\tpop\tst\n");
+ fprintf (file, "\treti\n");
+ }
+ else
+ {
+ if (frame_pointer_needed)
+ {
+ if ((size != 0)
+ || (current_function_args_size != 0)
+ || (optimize < 2))
+ {
+ /* R2 holds the return value. */
+ fprintf (file, "\tldi\t*-ar3(1),r2\n");
+
+ /* We already have the return value and the fp,
+ so we need to add those to the stack. */
+ size += 2;
+ delayed_jump = 1;
+ restore_count = 1;
+ dont_pop_ar3 = 1;
+ }
+ else
+ {
+ /* Since ar3 is not used for anything, we don't need to
+ pop it. */
+ dont_pop_ar3 = 1;
+ }
+ }
+ else
+ {
+ dont_pop_ar3 = 0; /* If we use ar3, we need to pop it */
+ if (size || current_function_args_size)
+ {
+ /* If we are ommitting the frame pointer, we still have
+ to make space for it so the offsets are correct
+ unless we don't use anything on the stack at all. */
+ size += 1;
+ }
+ }
+
+ /* Now get the number of instructions required to restore the
+ registers. */
+ for (regno = FIRST_PSEUDO_REGISTER - 1; regno >= 0; regno--)
+ {
+ if ((regs_ever_live[regno] && ! call_used_regs[regno])
+ && ((! dont_pop_ar3) || (regno != AR3_REGNO)))
+ {
+ restore_count++;
+ if (TARGET_PRESERVE_FLOAT
+ && ((regno == R6_REGNO) || (regno == R7_REGNO)))
+ restore_count++;
+ }
+ }
+
+ /* Get the number of instructions required to restore the stack. */
+ if (size > 32767)
+ restore_count += (TARGET_C3X ? 4 : 3);
+ else if (size != 0)
+ restore_count += 1;
+
+ if (delayed_jump && (restore_count < 3))
+ {
+ /* We don't have enough instructions to account for the delayed
+ branch, so put some nops in. */
+
+ fprintf (file, "\tbud\tr2\n");
+ while (restore_count < 3)
+ {
+ fprintf (file, "\tnop\n");
+ restore_count++;
+ }
+ restore_count = 0;
+ }
+
+ /* Now restore the saved registers, putting in the delayed branch
+ where required. */
+ for (regno = FIRST_PSEUDO_REGISTER - 1; regno >= 0; regno--)
+ {
+ if (regs_ever_live[regno] && ! call_used_regs[regno])
+ {
+ if (regno == AR3_REGNO && dont_pop_ar3)
+ continue;
+
+ if (delayed_jump && (restore_count == 3))
+ fprintf (file, "\tbud\tr2\n");
+
+ /* R6 and R7 are saved as floating point. */
+ if ((regno == R6_REGNO) || (regno == R7_REGNO))
+ {
+ fprintf (file, "\tpopf\t%s\n", float_reg_names[regno]);
+ if (TARGET_PRESERVE_FLOAT)
+ {
+ restore_count--;
+ if (delayed_jump && (restore_count == 3))
+ fprintf (file, "\tbud\tr2\n");
+ fprintf (file, "\tpop\t%s\n", reg_names[regno]);
+ }
+ }
+ else
+ fprintf (file, "\tpop\t%s\n", reg_names[regno]);
+ restore_count--;
+ }
+ }
+
+ if (delayed_jump && (restore_count == 3))
+ fprintf (file, "\tbud\tr2\n");
+
+ if (frame_pointer_needed)
+ {
+ if ((size != 0)
+ || (current_function_args_size != 0)
+ || (optimize < 2))
+ {
+ /* Restore the old FP. */
+ fprintf (file, "\tldi\t*ar3,ar3\n");
+ restore_count--;
+
+ if (delayed_jump && (restore_count == 3))
+ fprintf (file, "\tbud\tr2\n");
+ }
+ }
+
+ if (size > 32767)
+ {
+ /* Local vars are too big, it will take multiple operations
+ to decrement SP. */
+ if (TARGET_C3X)
+ {
+ fprintf (file, "\tldi\t%d,r3\n", size >> 16);
+ if (delayed_jump)
+ fprintf (file, "\tbud\tr2\n");
+ fprintf (file, "\tlsh\t16,r3\n");
+ }
+ else
+ fprintf (file, "\tldhi\t%d,r3\n", size >> 16);
+ fprintf (file, "\tor\t%d,r3\n", size & 0xffff);
+ fprintf (file, "\tsubi\tr3,sp\n");
+ }
+ else if (size != 0)
+ {
+ /* Local vars take up less than 32768 words, so we can directly
+ subtract the number. */
+ fprintf (file, "\tsubi\t%d,sp\n", size);
+ }
+
+ if (! delayed_jump)
+ fprintf (file, "\trets\n");
+ }
+}
+
+int
+c4x_null_epilogue_p ()
+{
+ int regno;
+
+ if (reload_completed
+ && ! c4x_assembler_function_p ()
+ && ! c4x_interrupt_function_p ()
+ && ! current_function_calls_alloca
+ && ! current_function_args_size
+ && ! (profile_block_flag == 2)
+ && ! (optimize < 2)
+ && ! get_frame_size ())
+ {
+ for (regno = FIRST_PSEUDO_REGISTER - 1; regno >= 0; regno--)
+ if (regs_ever_live[regno] && ! call_used_regs[regno]
+ && (regno != AR3_REGNO))
+ return 0;
+ return 1;
+ }
+ return 0;
+}
+
+int
+c4x_emit_move_sequence (operands, mode)
+ rtx *operands;
+ enum machine_mode mode;
+{
+ rtx op0 = operands[0];
+ rtx op1 = operands[1];
+
+ if (! reload_in_progress
+ && ! REG_P (op0)
+ && ! REG_P (op1)
+ && ! (stik_const_operand (op1, mode) && ! push_operand (op0, mode)))
+ op1 = force_reg (mode, op1);
+
+ if (GET_CODE (op1) == LO_SUM
+ && GET_MODE (op1) == Pmode
+ && dp_reg_operand (XEXP (op1, 0), mode))
+ {
+ /* expand_increment will sometimes create a LO_SUM immediate
+ address. */
+ op1 = XEXP (op1, 1);
+ }
+ else if (symbolic_address_operand (op1, mode))
+ {
+ if (TARGET_LOAD_ADDRESS)
+ {
+ /* Alias analysis seems to do a better job if we force
+ constant addresses to memory after reload. */
+ emit_insn (gen_load_immed_address (op0, op1));
+ return 1;
+ }
+ else
+ {
+ /* Stick symbol or label address into the constant pool. */
+ op1 = force_const_mem (Pmode, op1);
+ }
+ }
+ else if (mode == HFmode && CONSTANT_P (op1) && ! LEGITIMATE_CONSTANT_P (op1))
+ {
+ /* We could be a lot smarter about loading some of these
+ constants... */
+ op1 = force_const_mem (mode, op1);
+ }
+ else if (mode == QImode && CONSTANT_P (op1) && ! LEGITIMATE_CONSTANT_P (op1))
+ {
+ /* We shouldn't need this test if only emit_move_insn was called.
+ However, some routines call gen_move_insn which doesn't check that
+ the constants are legitimate. */
+ op1 = force_const_mem (mode, op1);
+ }
+ else if (mode == HImode && CONSTANT_P (op1) && ! LEGITIMATE_CONSTANT_P (op1))
+ {
+ /* We could load all sorts of constants in two goes by pulling all
+ sorts of tricks... The tricky thing is that we cannot clobber CC
+ so that stifles most of the obvious methods. */
+ op1 = force_const_mem (mode, op1);
+ }
+
+ /* Convert (MEM (SYMREF)) to a (MEM (LO_SUM (REG) (SYMREF)))
+ and emit associated (HIGH (SYMREF)) if large memory model.
+ c4x_legitimize_address could be used to do this,
+ perhaps by calling validize_address. */
+ if (! (reload_in_progress || reload_completed)
+ && GET_CODE (op1) == MEM
+ && symbolic_address_operand (XEXP (op1, 0), Pmode))
+ {
+ rtx dp_reg = gen_rtx_REG (Pmode, DP_REGNO);
+ if (! TARGET_SMALL)
+ emit_insn (gen_set_ldp (dp_reg, XEXP (op1, 0)));
+ op1 = change_address (op1, mode,
+ gen_rtx_LO_SUM (Pmode, dp_reg, XEXP (op1, 0)));
+ }
+
+ if (! (reload_in_progress || reload_completed)
+ && GET_CODE (op0) == MEM
+ && symbolic_address_operand (XEXP (op0, 0), Pmode))
+ {
+ rtx dp_reg = gen_rtx_REG (Pmode, DP_REGNO);
+ if (! TARGET_SMALL)
+ emit_insn (gen_set_ldp (dp_reg, XEXP (op0, 0)));
+ op0 = change_address (op0, mode,
+ gen_rtx_LO_SUM (Pmode, dp_reg, XEXP (op0, 0)));
+ }
+
+ if (GET_CODE (op0) == SUBREG
+ && mixed_subreg_operand (op0, mode))
+ {
+ /* We should only generate these mixed mode patterns
+ during RTL generation. If we need do it later on
+ then we'll have to emit patterns that won't clobber CC. */
+ if (reload_in_progress || reload_completed)
+ abort ();
+ if (GET_MODE (SUBREG_REG (op0)) == QImode)
+ op0 = SUBREG_REG (op0);
+ else if (GET_MODE (SUBREG_REG (op0)) == HImode)
+ {
+ op0 = copy_rtx (op0);
+ PUT_MODE (op0, QImode);
+ }
+ else
+ abort ();
+
+ if (mode == QFmode)
+ emit_insn (gen_storeqf_int_clobber (op0, op1));
+ else
+ abort ();
+ return 1;
+ }
+
+ if (GET_CODE (op1) == SUBREG
+ && mixed_subreg_operand (op1, mode))
+ {
+ /* We should only generate these mixed mode patterns
+ during RTL generation. If we need do it later on
+ then we'll have to emit patterns that won't clobber CC. */
+ if (reload_in_progress || reload_completed)
+ abort ();
+ if (GET_MODE (SUBREG_REG (op1)) == QImode)
+ op1 = SUBREG_REG (op1);
+ else if (GET_MODE (SUBREG_REG (op1)) == HImode)
+ {
+ op1 = copy_rtx (op1);
+ PUT_MODE (op1, QImode);
+ }
+ else
+ abort ();
+
+ if (mode == QFmode)
+ emit_insn (gen_loadqf_int_clobber (op0, op1));
+ else
+ abort ();
+ return 1;
+ }
+
+ /* Adjust operands in case we have modified them. */
+ operands[0] = op0;
+ operands[1] = op1;
+
+ /* Emit normal pattern. */
+ return 0;
+}
+
+
+void
+c4x_emit_libcall (name, code, dmode, smode, noperands, operands)
+ char *name;
+ enum rtx_code code;
+ enum machine_mode dmode;
+ enum machine_mode smode;
+ int noperands;
+ rtx *operands;
+{
+ rtx ret;
+ rtx insns;
+ rtx libcall;
+ rtx equiv;
+
+ start_sequence ();
+ libcall = gen_rtx_SYMBOL_REF (Pmode, name);
+ switch (noperands)
+ {
+ case 2:
+ ret = emit_library_call_value (libcall, NULL_RTX, 1, dmode, 1,
+ operands[1], smode);
+ equiv = gen_rtx (code, dmode, operands[1]);
+ break;
+
+ case 3:
+ ret = emit_library_call_value (libcall, NULL_RTX, 1, dmode, 2,
+ operands[1], smode, operands[2], smode);
+ equiv = gen_rtx (code, dmode, operands[1], operands[2]);
+ break;
+
+ default:
+ fatal ("c4x_emit_libcall: Bad number of operands");
+ }
+
+ insns = get_insns ();
+ end_sequence ();
+ emit_libcall_block (insns, operands[0], ret, equiv);
+}
+
+
+void
+c4x_emit_libcall3 (name, code, mode, operands)
+ const char *name;
+ enum rtx_code code;
+ enum machine_mode mode;
+ rtx *operands;
+{
+ return c4x_emit_libcall (name, code, mode, mode, 3, operands);
+}
+
+
+void
+c4x_emit_libcall_mulhi (name, code, mode, operands)
+ char *name;
+ enum rtx_code code;
+ enum machine_mode mode;
+ rtx *operands;
+{
+ rtx ret;
+ rtx insns;
+ rtx libcall;
+ rtx equiv;
+
+ start_sequence ();
+ libcall = gen_rtx_SYMBOL_REF (Pmode, name);
+ ret = emit_library_call_value (libcall, NULL_RTX, 1, mode, 2,
+ operands[1], mode, operands[2], mode);
+ equiv = gen_rtx_TRUNCATE (mode,
+ gen_rtx_LSHIFTRT (HImode,
+ gen_rtx_MULT (HImode,
+ gen_rtx (code, HImode, operands[1]),
+ gen_rtx (code, HImode, operands[2])),
+ GEN_INT (32)));
+ insns = get_insns ();
+ end_sequence ();
+ emit_libcall_block (insns, operands[0], ret, equiv);
+}
+
+
+enum reg_class
+c4x_preferred_reload_class (x, class)
+ rtx x ATTRIBUTE_UNUSED;
+ enum reg_class class;
+{
+ return class;
+}
+
+
+enum reg_class
+c4x_limit_reload_class (mode, class)
+ enum machine_mode mode ATTRIBUTE_UNUSED;
+ enum reg_class class;
+{
+ return class;
+}
+
+
+enum reg_class
+c4x_secondary_memory_needed (class1, class2, mode)
+ enum reg_class class1 ATTRIBUTE_UNUSED;
+ enum reg_class class2 ATTRIBUTE_UNUSED;
+ enum machine_mode mode ATTRIBUTE_UNUSED;
+{
+ return 0;
+}
+
+
+/* Set the SYMBOL_REF_FLAG for a function decl. However, wo do not
+ yet use this info. */
+void
+c4x_encode_section_info (decl)
+ tree decl;
+{
+#if 0
+ if (TREE_CODE (TREE_TYPE (decl)) == FUNCTION_TYPE)
+ SYMBOL_REF_FLAG (XEXP (DECL_RTL (decl), 0)) = 1;
+#else
+ if (TREE_CODE (decl) == FUNCTION_DECL)
+ SYMBOL_REF_FLAG (XEXP (DECL_RTL (decl), 0)) = 1;
+#endif
+}
+
+
+int
+c4x_check_legit_addr (mode, addr, strict)
+ enum machine_mode mode;
+ rtx addr;
+ int strict;
+{
+ rtx base = NULL_RTX; /* Base register (AR0-AR7) */
+ rtx indx = NULL_RTX; /* Index register (IR0,IR1) */
+ rtx disp = NULL_RTX; /* Displacement */
+ enum rtx_code code;
+
+ code = GET_CODE (addr);
+ switch (code)
+ {
+ /* Register indirect with auto increment/decrement. We don't
+ allow SP here---push_operand should recognise an operand
+ being pushed on the stack. */
+
+ case PRE_DEC:
+ case PRE_INC:
+ case POST_DEC:
+ if (mode != QImode && mode != QFmode)
+ return 0;
+
+ case POST_INC:
+ base = XEXP (addr, 0);
+ if (! REG_P (base))
+ return 0;
+ break;
+
+ case PRE_MODIFY:
+ case POST_MODIFY:
+ {
+ rtx op0 = XEXP (addr, 0);
+ rtx op1 = XEXP (addr, 1);
+
+ if (mode != QImode && mode != QFmode)
+ return 0;
+
+ if (! REG_P (op0)
+ || (GET_CODE (op1) != PLUS && GET_CODE (op1) != MINUS))
+ return 0;
+ base = XEXP (op1, 0);
+ if (base != op0)
+ return 0;
+ if (REG_P (XEXP (op1, 1)))
+ indx = XEXP (op1, 1);
+ else
+ disp = XEXP (op1, 1);
+ }
+ break;
+
+ /* Register indirect. */
+ case REG:
+ base = addr;
+ break;
+
+ /* Register indirect with displacement or index. */
+ case PLUS:
+ {
+ rtx op0 = XEXP (addr, 0);
+ rtx op1 = XEXP (addr, 1);
+ enum rtx_code code0 = GET_CODE (op0);
+
+ switch (code0)
+ {
+ case REG:
+ if (REG_P (op1))
+ {
+ base = op0; /* base + index */
+ indx = op1;
+ if (IS_INDEX_REGNO (base) || IS_ADDR_REGNO (indx))
+ {
+ base = op1;
+ indx = op0;
+ }
+ }
+ else
+ {
+ base = op0; /* base + displacement */
+ disp = op1;
+ }
+ break;
+
+ default:
+ return 0;
+ }
+ }
+ break;
+
+ /* Direct addressing with DP register. */
+ case LO_SUM:
+ {
+ rtx op0 = XEXP (addr, 0);
+ rtx op1 = XEXP (addr, 1);
+
+ /* HImode and HFmode direct memory references aren't truly
+ offsettable (consider case at end of data page). We
+ probably get better code by loading a pointer and using an
+ indirect memory reference. */
+ if (mode == HImode || mode == HFmode)
+ return 0;
+
+ if (!REG_P (op0) || REGNO (op0) != DP_REGNO)
+ return 0;
+
+ if ((GET_CODE (op1) == SYMBOL_REF || GET_CODE (op1) == LABEL_REF))
+ return 1;
+
+ if (GET_CODE (op1) == CONST)
+ {
+ addr = XEXP (op1, 0);
+
+ if (GET_CODE (addr) == PLUS
+ && (GET_CODE (XEXP (addr, 0)) == SYMBOL_REF
+ || GET_CODE (XEXP (addr, 0)) == LABEL_REF)
+ && GET_CODE (XEXP (addr, 1)) == CONST_INT)
+ return 1;
+ }
+ return 0;
+ }
+ break;
+
+ /* Direct addressing with some work for the assembler... */
+ case CONST:
+ /* Direct addressing. */
+ case LABEL_REF:
+ case SYMBOL_REF:
+ /* These need to be converted to a LO_SUM (...).
+ c4x_legitimize_address will fix them up. */
+ return 0;
+
+ /* Do not allow direct memory access to absolute addresses.
+ This is more pain than its worth, especially for the
+ small memory model where we can't guarantee that
+ this address is within the data page---we don't want
+ to modify the DP register in the small memory model,
+ even temporarily, since an interrupt can sneak in.... */
+ case CONST_INT:
+ return 0;
+
+ /* Indirect indirect addressing. */
+ case MEM:
+ return 0;
+
+ case CONST_DOUBLE:
+ fatal_insn ("Using CONST_DOUBLE for address", addr);
+
+ default:
+ return 0;
+ }
+
+ /* Validate the base register. */
+ if (base)
+ {
+ /* Check that the address is offsettable for HImode and HFmode. */
+ if (indx && (mode == HImode || mode == HFmode))
+ return 0;
+
+ /* Handle DP based stuff. */
+ if (REGNO (base) == DP_REGNO)
+ return 1;
+ if (strict && ! REGNO_OK_FOR_BASE_P (REGNO (base)))
+ return 0;
+ else if (! strict && ! IS_ADDR_OR_PSEUDO_REGNO (base))
+ return 0;
+ }
+
+ /* Now validate the index register. */
+ if (indx)
+ {
+ if (GET_CODE (indx) != REG)
+ return 0;
+ if (strict && ! REGNO_OK_FOR_INDEX_P (REGNO (indx)))
+ return 0;
+ else if (! strict && ! IS_INDEX_OR_PSEUDO_REGNO (indx))
+ return 0;
+ }
+
+ /* Validate displacement. */
+ if (disp)
+ {
+ if (GET_CODE (disp) != CONST_INT)
+ return 0;
+ if (mode == HImode || mode == HFmode)
+ {
+ /* The offset displacement must be legitimate. */
+ if (! IS_DISP8_OFF_CONST (INTVAL (disp)))
+ return 0;
+ }
+ else
+ {
+ if (! IS_DISP8_CONST (INTVAL (disp)))
+ return 0;
+ }
+ /* Can't add an index with a disp. */
+ if (indx)
+ return 0;
+ }
+ return 1;
+}
+
+
+rtx
+c4x_legitimize_address (orig, mode)
+ rtx orig ATTRIBUTE_UNUSED;
+ enum machine_mode mode ATTRIBUTE_UNUSED;
+{
+ if (GET_CODE (orig) == SYMBOL_REF
+ || GET_CODE (orig) == LABEL_REF)
+ {
+ if (mode == HImode || mode == HFmode)
+ {
+ /* We need to force the address into
+ a register so that it is offsettable. */
+ rtx addr_reg = gen_reg_rtx (Pmode);
+ emit_move_insn (addr_reg, orig);
+ return addr_reg;
+ }
+ else
+ {
+ rtx dp_reg = gen_rtx_REG (Pmode, DP_REGNO);
+
+ if (! TARGET_SMALL)
+ emit_insn (gen_set_ldp (dp_reg, orig));
+
+ return gen_rtx_LO_SUM (Pmode, dp_reg, orig);
+ }
+ }
+
+ return NULL_RTX;
+}
+
+
+/* Provide the costs of an addressing mode that contains ADDR.
+ If ADDR is not a valid address, its cost is irrelevant.
+ This is used in cse and loop optimisation to determine
+ if it is worthwhile storing a common address into a register.
+ Unfortunately, the C4x address cost depends on other operands. */
+
+int
+c4x_address_cost (addr)
+rtx addr;
+{
+ switch (GET_CODE (addr))
+ {
+ case REG:
+ return 1;
+
+ case POST_INC:
+ case POST_DEC:
+ case PRE_INC:
+ case PRE_DEC:
+ return 1;
+
+ /* These shouldn't be directly generated. */
+ case SYMBOL_REF:
+ case LABEL_REF:
+ case CONST:
+ return 10;
+
+ case LO_SUM:
+ {
+ rtx op1 = XEXP (addr, 1);
+
+ if (GET_CODE (op1) == LABEL_REF || GET_CODE (op1) == SYMBOL_REF)
+ return TARGET_SMALL ? 3 : 4;
+
+ if (GET_CODE (op1) == CONST)
+ {
+ rtx offset = const0_rtx;
+
+ op1 = eliminate_constant_term (op1, &offset);
+
+ /* ??? These costs need rethinking... */
+ if (GET_CODE (op1) == LABEL_REF)
+ return 3;
+
+ if (GET_CODE (op1) != SYMBOL_REF)
+ return 4;
+
+ if (INTVAL (offset) == 0)
+ return 3;
+
+ return 4;
+ }
+ fatal_insn ("c4x_address_cost: Invalid addressing mode", addr);
+ }
+ break;
+
+ case PLUS:
+ {
+ register rtx op0 = XEXP (addr, 0);
+ register rtx op1 = XEXP (addr, 1);
+
+ if (GET_CODE (op0) != REG)
+ break;
+
+ switch (GET_CODE (op1))
+ {
+ default:
+ break;
+
+ case REG:
+ /* This cost for REG+REG must be greater than the cost
+ for REG if we want autoincrement addressing modes. */
+ return 2;
+
+ case CONST_INT:
+ if (IS_DISP1_CONST (INTVAL (op1)))
+ return 1;
+
+ if (! TARGET_C3X && IS_UINT5_CONST (INTVAL (op1)))
+ return 2;
+
+ return 3;
+ }
+ }
+ default:
+ }
+
+ return 4;
+}
+
+
+rtx
+c4x_gen_compare_reg (code, x, y)
+ enum rtx_code code;
+ rtx x, y;
+{
+ enum machine_mode mode = SELECT_CC_MODE (code, x, y);
+ rtx cc_reg;
+
+ if (mode == CC_NOOVmode
+ && (code == LE || code == GE || code == LT || code == GT))
+ return NULL_RTX;
+
+ cc_reg = gen_rtx_REG (mode, ST_REGNO);
+ emit_insn (gen_rtx_SET (VOIDmode, cc_reg,
+ gen_rtx_COMPARE (mode, x, y)));
+ return cc_reg;
+}
+
+char *
+c4x_output_cbranch (form, seq)
+ char *form;
+ rtx seq;
+{
+ int delayed = 0;
+ int annultrue = 0;
+ int annulfalse = 0;
+ rtx delay;
+ char *cp;
+ static char str[100];
+
+ if (final_sequence)
+ {
+ delay = XVECEXP (final_sequence, 0, 1);
+ delayed = ! INSN_ANNULLED_BRANCH_P (seq);
+ annultrue = INSN_ANNULLED_BRANCH_P (seq) && ! INSN_FROM_TARGET_P (delay);
+ annulfalse = INSN_ANNULLED_BRANCH_P (seq) && INSN_FROM_TARGET_P (delay);
+ }
+ strcpy (str, form);
+ cp = &str [strlen (str)];
+ if (delayed)
+ {
+ *cp++ = '%';
+ *cp++ = '#';
+ }
+ if (annultrue)
+ {
+ *cp++ = 'a';
+ *cp++ = 't';
+ }
+ if (annulfalse)
+ {
+ *cp++ = 'a';
+ *cp++ = 'f';
+ }
+ *cp++ = '\t';
+ *cp++ = '%';
+ *cp++ = 'l';
+ *cp++ = '1';
+ *cp = 0;
+ return str;
+}
+
+void
+c4x_print_operand (file, op, letter)
+ FILE *file; /* file to write to */
+ rtx op; /* operand to print */
+ int letter; /* %<letter> or 0 */
+{
+ rtx op1;
+ enum rtx_code code;
+
+ switch (letter)
+ {
+ case '#': /* delayed */
+ if (final_sequence)
+ asm_fprintf (file, "d");
+ return;
+ }
+
+ code = GET_CODE (op);
+ switch (letter)
+ {
+ case 'A': /* direct address */
+ if (code == CONST_INT || code == SYMBOL_REF)
+ asm_fprintf (file, "@");
+ break;
+
+ case 'H': /* sethi */
+ output_addr_const (file, op);
+ return;
+
+ case 'I': /* reversed condition */
+ code = reverse_condition (code);
+ break;
+
+ case 'L': /* log 2 of constant */
+ if (code != CONST_INT)
+ fatal_insn ("c4x_print_operand: %%L inconsistency", op);
+ fprintf (file, "%d", exact_log2 (INTVAL (op)));
+ return;
+
+ case 'N': /* ones complement of small constant */
+ if (code != CONST_INT)
+ fatal_insn ("c4x_print_operand: %%N inconsistency", op);
+ fprintf (file, "%d", ~INTVAL (op));
+ return;
+
+ case 'K': /* generate ldp(k) if direct address */
+ if (! TARGET_SMALL
+ && code == MEM
+ && GET_CODE (XEXP (op, 0)) == LO_SUM
+ && GET_CODE (XEXP (XEXP (op, 0), 0)) == REG
+ && REGNO (XEXP (XEXP (op, 0), 0)) == DP_REGNO)
+ {
+ op1 = XEXP (XEXP (op, 0), 1);
+ if (GET_CODE(op1) == CONST_INT || GET_CODE(op1) == SYMBOL_REF)
+ {
+ asm_fprintf (file, "\t%s\t", TARGET_C3X ? "ldp" : "ldpk");
+ output_address (XEXP (adj_offsettable_operand (op, 1), 0));
+ asm_fprintf (file, "\n");
+ }
+ }
+ return;
+
+ case 'M': /* generate ldp(k) if direct address */
+ if (! TARGET_SMALL /* only used in asm statements */
+ && code == MEM
+ && (GET_CODE (XEXP (op, 0)) == CONST
+ || GET_CODE (XEXP (op, 0)) == SYMBOL_REF))
+ {
+ asm_fprintf (file, "%s\t", TARGET_C3X ? "ldp" : "ldpk");
+ output_address (XEXP (op, 0));
+ asm_fprintf (file, "\n\t");
+ }
+ return;
+
+ case 'O': /* offset address */
+ if (code == MEM && c4x_autoinc_operand (op, Pmode))
+ break;
+ else if (code == MEM)
+ output_address (XEXP (adj_offsettable_operand (op, 1), 0));
+ else if (code == REG)
+ fprintf (file, "%s", reg_names[REGNO (op) + 1]);
+ else
+ fatal_insn ("c4x_print_operand: %%O inconsistency", op);
+ return;
+
+ case 'C': /* call */
+ break;
+
+ case 'U': /* call/callu */
+ if (code != SYMBOL_REF)
+ asm_fprintf (file, "u");
+ return;
+
+ default:
+ break;
+ }
+
+ switch (code)
+ {
+ case REG:
+ if (GET_MODE_CLASS (GET_MODE (op)) == MODE_FLOAT)
+ fprintf (file, "%s", float_reg_names[REGNO (op)]);
+ else
+ fprintf (file, "%s", reg_names[REGNO (op)]);
+ break;
+
+ case MEM:
+ output_address (XEXP (op, 0));
+ break;
+
+ case CONST_DOUBLE:
+ {
+ char str[30];
+ REAL_VALUE_TYPE r;
+
+ REAL_VALUE_FROM_CONST_DOUBLE (r, op);
+ REAL_VALUE_TO_DECIMAL (r, "%20f", str);
+ fprintf (file, "%s", str);
+ }
+ break;
+
+ case CONST_INT:
+ fprintf (file, "%d", INTVAL (op));
+ break;
+
+ case NE:
+ asm_fprintf (file, "ne");
+ break;
+
+ case EQ:
+ asm_fprintf (file, "eq");
+ break;
+
+ case GE:
+ asm_fprintf (file, "ge");
+ break;
+
+ case GT:
+ asm_fprintf (file, "gt");
+ break;
+
+ case LE:
+ asm_fprintf (file, "le");
+ break;
+
+ case LT:
+ asm_fprintf (file, "lt");
+ break;
+
+ case GEU:
+ asm_fprintf (file, "hs");
+ break;
+
+ case GTU:
+ asm_fprintf (file, "hi");
+ break;
+
+ case LEU:
+ asm_fprintf (file, "ls");
+ break;
+
+ case LTU:
+ asm_fprintf (file, "lo");
+ break;
+
+ case SYMBOL_REF:
+ output_addr_const (file, op);
+ break;
+
+ case CONST:
+ output_addr_const (file, XEXP (op, 0));
+ break;
+
+ case CODE_LABEL:
+ break;
+
+ default:
+ fatal_insn ("c4x_print_operand: Bad operand case", op);
+ break;
+ }
+}
+
+
+void
+c4x_print_operand_address (file, addr)
+ FILE *file;
+ rtx addr;
+{
+ switch (GET_CODE (addr))
+ {
+ case REG:
+ fprintf (file, "*%s", reg_names[REGNO (addr)]);
+ break;
+
+ case PRE_DEC:
+ fprintf (file, "*--%s", reg_names[REGNO (XEXP (addr, 0))]);
+ break;
+
+ case POST_INC:
+ fprintf (file, "*%s++", reg_names[REGNO (XEXP (addr, 0))]);
+ break;
+
+ case POST_MODIFY:
+ {
+ rtx op0 = XEXP (XEXP (addr, 1), 0);
+ rtx op1 = XEXP (XEXP (addr, 1), 1);
+
+ if (GET_CODE (XEXP (addr, 1)) == PLUS && REG_P (op1))
+ fprintf (file, "*%s++(%s)", reg_names[REGNO (op0)],
+ reg_names[REGNO (op1)]);
+ else if (GET_CODE (XEXP (addr, 1)) == PLUS && INTVAL (op1) > 0)
+ fprintf (file, "*%s++(%d)", reg_names[REGNO (op0)],
+ INTVAL (op1));
+ else if (GET_CODE (XEXP (addr, 1)) == PLUS && INTVAL (op1) < 0)
+ fprintf (file, "*%s--(%d)", reg_names[REGNO (op0)],
+ -INTVAL (op1));
+ else if (GET_CODE (XEXP (addr, 1)) == MINUS && REG_P (op1))
+ fprintf (file, "*%s--(%s)", reg_names[REGNO (op0)],
+ reg_names[REGNO (op1)]);
+ else
+ fatal_insn ("c4x_print_operand_address: Bad post_modify", addr);
+ }
+ break;
+
+ case PRE_MODIFY:
+ {
+ rtx op0 = XEXP (XEXP (addr, 1), 0);
+ rtx op1 = XEXP (XEXP (addr, 1), 1);
+
+ if (GET_CODE (XEXP (addr, 1)) == PLUS && REG_P (op1))
+ fprintf (file, "*++%s(%s)", reg_names[REGNO (op0)],
+ reg_names[REGNO (op1)]);
+ else if (GET_CODE (XEXP (addr, 1)) == PLUS && INTVAL (op1) > 0)
+ fprintf (file, "*++%s(%d)", reg_names[REGNO (op0)],
+ INTVAL (op1));
+ else if (GET_CODE (XEXP (addr, 1)) == PLUS && INTVAL (op1) < 0)
+ fprintf (file, "*--%s(%d)", reg_names[REGNO (op0)],
+ -INTVAL (op1));
+ else if (GET_CODE (XEXP (addr, 1)) == MINUS && REG_P (op1))
+ fprintf (file, "*--%s(%s)", reg_names[REGNO (op0)],
+ reg_names[REGNO (op1)]);
+ else
+ fatal_insn ("c4x_print_operand_address: Bad pre_modify", addr);
+ }
+ break;
+
+ case PRE_INC:
+ fprintf (file, "*++%s", reg_names[REGNO (XEXP (addr, 0))]);
+ break;
+
+ case POST_DEC:
+ fprintf (file, "*%s--", reg_names[REGNO (XEXP (addr, 0))]);
+ break;
+
+ case PLUS: /* Indirect with displacement. */
+ {
+ rtx op0 = XEXP (addr, 0);
+ rtx op1 = XEXP (addr, 1);
+
+ if (REG_P (op0))
+ {
+ if (REG_P (op1))
+ {
+ if (IS_INDEX_REGNO (op0))
+ {
+ fprintf (file, "*+%s(%s)",
+ reg_names[REGNO (op1)],
+ reg_names[REGNO (op0)]); /* index + base */
+ }
+ else
+ {
+ fprintf (file, "*+%s(%s)",
+ reg_names[REGNO (op0)],
+ reg_names[REGNO (op1)]); /* base + index */
+ }
+ }
+ else if (INTVAL (op1) < 0)
+ {
+ fprintf (file, "*-%s(%d)",
+ reg_names[REGNO (op0)],
+ -INTVAL (op1)); /* base - displacement */
+ }
+ else
+ {
+ fprintf (file, "*+%s(%d)",
+ reg_names[REGNO (op0)],
+ INTVAL (op1)); /* base + displacement */
+ }
+ }
+ else
+ fatal_insn ("c4x_print_operand_address: Bad operand case", addr);
+ }
+ break;
+
+ case LO_SUM:
+ {
+ rtx op0 = XEXP (addr, 0);
+ rtx op1 = XEXP (addr, 1);
+
+ if (REG_P (op0) && REGNO (op0) == DP_REGNO)
+ c4x_print_operand_address (file, op1);
+ else
+ fatal_insn ("c4x_print_operand_address: Bad operand case", addr);
+ }
+ break;
+
+ case CONST:
+ case SYMBOL_REF:
+ case LABEL_REF:
+ fprintf (file, "@");
+ output_addr_const (file, addr);
+ break;
+
+ /* We shouldn't access CONST_INT addresses. */
+ case CONST_INT:
+
+ default:
+ fatal_insn ("c4x_print_operand_address: Bad operand case", addr);
+ break;
+ }
+}
+
+/* Return nonzero if the floating point operand will fit
+ in the immediate field. */
+static int
+c4x_immed_float_p (op)
+ rtx op;
+{
+ long convval[2];
+ int exponent;
+ REAL_VALUE_TYPE r;
+
+ REAL_VALUE_FROM_CONST_DOUBLE (r, op);
+ if (GET_MODE (op) == HFmode)
+ REAL_VALUE_TO_TARGET_DOUBLE (r, convval);
+ else
+ {
+ REAL_VALUE_TO_TARGET_SINGLE (r, convval[0]);
+ convval[1] = 0;
+ }
+
+ /* sign extend exponent */
+ exponent = (((convval[0] >> 24) & 0xff) ^ 0x80) - 0x80;
+ if (exponent == -128)
+ return 1; /* 0.0 */
+ if ((convval[0] & 0x00000fff) != 0 || convval[1] != 0)
+ return 0; /* Precision doesn't fit */
+ return (exponent <= 7) /* Positive exp */
+ && (exponent >= -7); /* Negative exp */
+}
+
+/* The last instruction in a repeat block cannot be a Bcond, DBcound,
+ CALL, CALLCond, TRAPcond, RETIcond, RETScond, IDLE, RPTB or RPTS.
+
+ None of the last four instructions from the bottom of the block can
+ be a BcondD, BRD, DBcondD, RPTBD, LAJ, LAJcond, LATcond, BcondAF,
+ BcondAT or RETIcondD.
+
+ This routine scans the four previous insns for a jump insn, and if
+ one is found, returns 1 so that we bung in a nop instruction.
+ This simple minded strategy will add a nop, when it may not
+ be required. Say when there is a JUMP_INSN near the end of the
+ block that doesn't get converted into a delayed branch.
+
+ Note that we cannot have a call insn, since we don't generate
+ repeat loops with calls in them (although I suppose we could, but
+ there's no benefit.)
+
+ !!! FIXME. The rptb_top insn may be sucked into a SEQUENCE. */
+
+int
+c4x_rptb_nop_p (insn)
+ rtx insn;
+{
+ rtx start_label;
+ int i;
+
+ /* Extract the start label from the jump pattern (rptb_end). */
+ start_label = XEXP (XEXP (SET_SRC (XVECEXP (PATTERN (insn), 0, 0)), 1), 0);
+
+ /* If there is a label at the end of the loop we must insert
+ a NOP. */
+ do {
+ insn = previous_insn (insn);
+ } while (GET_CODE (insn) == NOTE
+ || GET_CODE (insn) == USE
+ || GET_CODE (insn) == CLOBBER);
+ if (GET_CODE (insn) == CODE_LABEL)
+ return 1;
+
+ for (i = 0; i < 4; i++)
+ {
+ /* Search back for prev non-note and non-label insn. */
+ while (GET_CODE (insn) == NOTE || GET_CODE (insn) == CODE_LABEL
+ || GET_CODE (insn) == USE || GET_CODE (insn) == CLOBBER)
+ {
+ if (insn == start_label)
+ return i == 0;
+
+ insn = previous_insn (insn);
+ };
+
+ /* If we have a jump instruction we should insert a NOP. If we
+ hit repeat block top we should only insert a NOP if the loop
+ is empty. */
+ if (GET_CODE (insn) == JUMP_INSN)
+ return 1;
+ insn = previous_insn (insn);
+ }
+ return 0;
+}
+
+
+void
+c4x_rptb_insert (insn)
+ rtx insn;
+{
+ rtx end_label;
+ rtx start_label;
+ rtx count_reg;
+
+ /* If the count register has not been allocated to RC, say if
+ there is a movstr pattern in the loop, then do not insert a
+ RPTB instruction. Instead we emit a decrement and branch
+ at the end of the loop. */
+ count_reg = XEXP (XEXP (SET_SRC (XVECEXP (PATTERN (insn), 0, 0)), 0), 0);
+ if (REGNO (count_reg) != RC_REGNO)
+ return;
+
+ /* Extract the start label from the jump pattern (rptb_end). */
+ start_label = XEXP (XEXP (SET_SRC (XVECEXP (PATTERN (insn), 0, 0)), 1), 0);
+
+ /* We'll have to update the basic blocks. */
+ end_label = gen_label_rtx ();
+ emit_label_after (end_label, insn);
+
+ for (; insn; insn = PREV_INSN (insn))
+ if (insn == start_label)
+ break;
+ if (! insn)
+ fatal_insn ("c4x_rptb_insert: Cannot find start label", start_label);
+
+ /* We'll have to update the basic blocks. */
+ emit_insn_before (gen_rptb_top (start_label, end_label), insn);
+}
+
+
+/* This function is a C4x special called immediately before delayed
+ branch scheduling. We fix up RTPB style loops that didn't get RC
+ allocated as the loop counter. */
+
+void
+c4x_process_after_reload (first)
+ rtx first;
+{
+ rtx insn;
+
+ for (insn = first; insn; insn = NEXT_INSN (insn))
+ {
+ /* Look for insn. */
+ if (GET_RTX_CLASS (GET_CODE (insn)) == 'i')
+ {
+ int insn_code_number;
+
+ insn_code_number = recog_memoized (insn);
+
+ if (insn_code_number < 0)
+ continue;
+
+ /* Insert the RTX for RPTB at the top of the loop
+ and a label at the end of the loop. */
+ if (insn_code_number == CODE_FOR_rptb_end)
+ c4x_rptb_insert(insn);
+
+ /* We split all insns here if they have a # for the output
+ template. */
+
+ if (1)
+ {
+ const char *template;
+
+ template = insn_template[insn_code_number];
+ if (template && template[0] == '#' && template[1] == '\0')
+ {
+ rtx new = try_split (PATTERN(insn), insn, 0);
+
+ /* If we didn't split the insn, go away. */
+ if (new == insn && PATTERN (new) == PATTERN(insn))
+ fatal_insn ("Couldn't split pattern", insn);
+
+ PUT_CODE (insn, NOTE);
+ NOTE_SOURCE_FILE (insn) = 0;
+ NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED;
+ insn = new;
+ }
+ }
+ }
+ }
+}
+
+
+static int
+c4x_a_register (op)
+ rtx op;
+{
+ return REG_P (op) && IS_ADDR_OR_PSEUDO_REGNO (op);
+}
+
+
+static int
+c4x_x_register (op)
+ rtx op;
+{
+ return REG_P (op) && IS_INDEX_OR_PSEUDO_REGNO (op);
+}
+
+
+static int
+c4x_immed_int_constant (op)
+ rtx op;
+{
+ if (GET_CODE (op) != CONST_INT)
+ return 0;
+
+ return GET_MODE (op) == VOIDmode
+ || GET_MODE_CLASS (op) == MODE_INT
+ || GET_MODE_CLASS (op) == MODE_PARTIAL_INT;
+}
+
+
+static int
+c4x_immed_float_constant (op)
+ rtx op;
+{
+ if (GET_CODE (op) != CONST_DOUBLE)
+ return 0;
+
+ if (GET_CODE (XEXP (op, 0)) == MEM)
+ return 0;
+
+ return GET_MODE (op) == QFmode || GET_MODE (op) == HFmode;
+}
+
+
+int
+c4x_H_constant (op)
+ rtx op;
+{
+ return c4x_immed_float_constant (op) && c4x_immed_float_p (op);
+}
+
+
+int
+c4x_I_constant (op)
+ rtx op;
+{
+ return c4x_immed_int_constant (op) && IS_INT16_CONST (INTVAL (op));
+}
+
+
+int
+c4x_J_constant (op)
+ rtx op;
+{
+ if (TARGET_C3X)
+ return 0;
+ return c4x_immed_int_constant (op) && IS_INT8_CONST (INTVAL (op));
+}
+
+
+static int
+c4x_K_constant (op)
+ rtx op;
+{
+ if (TARGET_C3X || ! c4x_immed_int_constant (op))
+ return 0;
+ return IS_INT5_CONST (INTVAL (op));
+}
+
+
+int
+c4x_L_constant (op)
+ rtx op;
+{
+ return c4x_immed_int_constant (op) && IS_UINT16_CONST (INTVAL (op));
+}
+
+
+static int
+c4x_N_constant (op)
+ rtx op;
+{
+ return c4x_immed_int_constant (op) && IS_NOT_UINT16_CONST (INTVAL (op));
+}
+
+
+static int
+c4x_O_constant (op)
+ rtx op;
+{
+ return c4x_immed_int_constant (op) && IS_HIGH_CONST (INTVAL (op));
+}
+
+
+/* The constraints do not have to check the register class,
+ except when needed to discriminate between the constraints.
+ The operand has been checked by the predicates to be valid. */
+
+/* ARx + 9-bit signed const or IRn
+ *ARx, *+ARx(n), *-ARx(n), *+ARx(IRn), *-Arx(IRn) for -256 < n < 256
+ We don't include the pre/post inc/dec forms here since
+ they are handled by the <> constraints. */
+
+int
+c4x_Q_constraint (op)
+ rtx op;
+{
+ enum machine_mode mode = GET_MODE (op);
+
+ if (GET_CODE (op) != MEM)
+ return 0;
+ op = XEXP (op, 0);
+ switch (GET_CODE (op))
+ {
+ case REG:
+ return 1;
+
+ case PLUS:
+ {
+ rtx op0 = XEXP (op, 0);
+ rtx op1 = XEXP (op, 1);
+
+ if (! REG_P (op0))
+ return 0;
+
+ if (REG_P (op1))
+ return 1;
+
+ if (GET_CODE (op1) != CONST_INT)
+ return 0;
+
+ /* HImode and HFmode must be offsettable. */
+ if (mode == HImode || mode == HFmode)
+ return IS_DISP8_OFF_CONST (INTVAL (op1));
+
+ return IS_DISP8_CONST (INTVAL (op1));
+ }
+ break;
+
+ default:
+ break;
+ }
+ return 0;
+}
+
+
+/* ARx + 5-bit unsigned const
+ *ARx, *+ARx(n) for n < 32 */
+
+int
+c4x_R_constraint (op)
+ rtx op;
+{
+ enum machine_mode mode = GET_MODE (op);
+
+ if (TARGET_C3X)
+ return 0;
+ if (GET_CODE (op) != MEM)
+ return 0;
+ op = XEXP (op, 0);
+ switch (GET_CODE (op))
+ {
+ case REG:
+ return 1;
+
+ case PLUS:
+ {
+ rtx op0 = XEXP (op, 0);
+ rtx op1 = XEXP (op, 1);
+
+ if (! REG_P (op0))
+ return 0;
+
+ if (GET_CODE (op1) != CONST_INT)
+ return 0;
+
+ /* HImode and HFmode must be offsettable. */
+ if (mode == HImode || mode == HFmode)
+ return IS_UINT5_CONST (INTVAL (op1) + 1);
+
+ return IS_UINT5_CONST (INTVAL (op1));
+ }
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+
+static int
+c4x_R_indirect (op)
+ rtx op;
+{
+ enum machine_mode mode = GET_MODE (op);
+
+ if (TARGET_C3X || GET_CODE (op) != MEM)
+ return 0;
+
+ op = XEXP (op, 0);
+ switch (GET_CODE (op))
+ {
+ case REG:
+ return IS_ADDR_OR_PSEUDO_REGNO (op);
+
+ case PLUS:
+ {
+ rtx op0 = XEXP (op, 0);
+ rtx op1 = XEXP (op, 1);
+
+ /* HImode and HFmode must be offsettable. */
+ if (mode == HImode || mode == HFmode)
+ return IS_ADDR_OR_PSEUDO_REGNO (op0)
+ && GET_CODE (op1) == CONST_INT
+ && IS_UINT5_CONST (INTVAL (op1) + 1);
+
+ return REG_P (op0)
+ && IS_ADDR_OR_PSEUDO_REGNO (op0)
+ && GET_CODE (op1) == CONST_INT
+ && IS_UINT5_CONST (INTVAL (op1));
+ }
+ break;
+
+ default:
+ break;
+ }
+ return 0;
+}
+
+
+/* ARx + 1-bit unsigned const or IRn
+ *ARx, *+ARx(1), *-ARx(1), *+ARx(IRn), *-Arx(IRn)
+ We don't include the pre/post inc/dec forms here since
+ they are handled by the <> constraints. */
+
+int
+c4x_S_constraint (op)
+ rtx op;
+{
+ enum machine_mode mode = GET_MODE (op);
+ if (GET_CODE (op) != MEM)
+ return 0;
+ op = XEXP (op, 0);
+ switch (GET_CODE (op))
+ {
+ case REG:
+ return 1;
+
+ case PRE_MODIFY:
+ case POST_MODIFY:
+ {
+ rtx op0 = XEXP (op, 0);
+ rtx op1 = XEXP (op, 1);
+
+ if ((GET_CODE (op1) != PLUS && GET_CODE (op1) != MINUS)
+ || (op0 != XEXP (op1, 0)))
+ return 0;
+
+ op0 = XEXP (op1, 0);
+ op1 = XEXP (op1, 1);
+ return REG_P (op0) && REG_P (op1);
+ /* pre or post_modify with a displacement of 0 or 1
+ should not be generated. */
+ }
+ break;
+
+ case PLUS:
+ {
+ rtx op0 = XEXP (op, 0);
+ rtx op1 = XEXP (op, 1);
+
+ if (!REG_P (op0))
+ return 0;
+
+ if (REG_P (op1))
+ return 1;
+
+ if (GET_CODE (op1) != CONST_INT)
+ return 0;
+
+ /* HImode and HFmode must be offsettable. */
+ if (mode == HImode || mode == HFmode)
+ return IS_DISP1_OFF_CONST (INTVAL (op1));
+
+ return IS_DISP1_CONST (INTVAL (op1));
+ }
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+
+static int
+c4x_S_indirect (op)
+ rtx op;
+{
+ enum machine_mode mode = GET_MODE (op);
+ if (GET_CODE (op) != MEM)
+ return 0;
+
+ op = XEXP (op, 0);
+ switch (GET_CODE (op))
+ {
+ case PRE_DEC:
+ case POST_DEC:
+ if (mode != QImode && mode != QFmode)
+ return 0;
+ case PRE_INC:
+ case POST_INC:
+ op = XEXP (op, 0);
+
+ case REG:
+ return IS_ADDR_OR_PSEUDO_REGNO (op);
+
+ case PRE_MODIFY:
+ case POST_MODIFY:
+ {
+ rtx op0 = XEXP (op, 0);
+ rtx op1 = XEXP (op, 1);
+
+ if (mode != QImode && mode != QFmode)
+ return 0;
+
+ if ((GET_CODE (op1) != PLUS && GET_CODE (op1) != MINUS)
+ || (op0 != XEXP (op1, 0)))
+ return 0;
+
+ op0 = XEXP (op1, 0);
+ op1 = XEXP (op1, 1);
+ return REG_P (op0) && IS_ADDR_OR_PSEUDO_REGNO (op0)
+ && REG_P (op1) && IS_INDEX_OR_PSEUDO_REGNO (op1);
+ /* pre or post_modify with a displacement of 0 or 1
+ should not be generated. */
+ }
+
+ case PLUS:
+ {
+ rtx op0 = XEXP (op, 0);
+ rtx op1 = XEXP (op, 1);
+
+ if (REG_P (op0))
+ {
+ /* HImode and HFmode must be offsettable. */
+ if (mode == HImode || mode == HFmode)
+ return IS_ADDR_OR_PSEUDO_REGNO (op0)
+ && GET_CODE (op1) == CONST_INT
+ && IS_DISP1_OFF_CONST (INTVAL (op1));
+
+ if (REG_P (op1))
+ return (IS_INDEX_OR_PSEUDO_REGNO (op1)
+ && IS_ADDR_OR_PSEUDO_REGNO (op0))
+ || (IS_ADDR_OR_PSEUDO_REGNO (op1)
+ && IS_INDEX_OR_PSEUDO_REGNO (op0));
+
+ return IS_ADDR_OR_PSEUDO_REGNO (op0)
+ && GET_CODE (op1) == CONST_INT
+ && IS_DISP1_CONST (INTVAL (op1));
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+ return 0;
+}
+
+
+/* Direct memory operand. */
+
+int
+c4x_T_constraint (op)
+ rtx op;
+{
+ if (GET_CODE (op) != MEM)
+ return 0;
+ op = XEXP (op, 0);
+
+ if (GET_CODE (op) != LO_SUM)
+ {
+ /* Allow call operands. */
+ return GET_CODE (op) == SYMBOL_REF
+ && GET_MODE (op) == Pmode
+ && SYMBOL_REF_FLAG (op);
+ }
+
+ /* HImode and HFmode are not offsettable. */
+ if (GET_MODE (op) == HImode || GET_CODE (op) == HFmode)
+ return 0;
+
+ if ((GET_CODE (XEXP (op, 0)) == REG)
+ && (REGNO (XEXP (op, 0)) == DP_REGNO))
+ return c4x_U_constraint (XEXP (op, 1));
+
+ return 0;
+}
+
+
+/* Symbolic operand. */
+
+int
+c4x_U_constraint (op)
+ rtx op;
+{
+ /* Don't allow direct addressing to an arbitrary constant. */
+ if (GET_CODE (op) == CONST
+ && GET_CODE (XEXP (op, 0)) == PLUS
+ && (GET_CODE (XEXP (XEXP (op, 0), 0)) == SYMBOL_REF
+ || GET_CODE (XEXP (XEXP (op, 0), 0)) == LABEL_REF)
+ && GET_CODE (XEXP (XEXP (op, 0), 1)) == CONST_INT)
+ return 1;
+
+ return GET_CODE (op) == SYMBOL_REF || GET_CODE (op) == LABEL_REF;
+}
+
+
+int
+c4x_autoinc_operand (op, mode)
+ rtx op;
+ enum machine_mode mode ATTRIBUTE_UNUSED;
+{
+ if (GET_CODE (op) == MEM)
+ {
+ enum rtx_code code = GET_CODE (XEXP (op, 0));
+
+ if (code == PRE_INC
+ || code == PRE_DEC
+ || code == POST_INC
+ || code == POST_DEC
+ || code == PRE_MODIFY
+ || code == POST_MODIFY
+ )
+ return 1;
+ }
+ return 0;
+}
+
+
+/* Match any operand. */
+
+int
+any_operand (op, mode)
+ register rtx op ATTRIBUTE_UNUSED;
+ enum machine_mode mode ATTRIBUTE_UNUSED;
+{
+ return 1;
+}
+
+
+/* Nonzero if OP is a floating point value with value 0.0. */
+
+int
+fp_zero_operand (op)
+ rtx op;
+{
+ REAL_VALUE_TYPE r;
+
+ REAL_VALUE_FROM_CONST_DOUBLE (r, op);
+ return REAL_VALUES_EQUAL (r, dconst0);
+}
+
+
+int
+const_operand (op, mode)
+ register rtx op;
+ register enum machine_mode mode;
+{
+ switch (mode)
+ {
+ case QFmode:
+ case HFmode:
+ if (GET_CODE (op) != CONST_DOUBLE
+ || GET_MODE (op) != mode
+ || GET_MODE_CLASS (mode) != MODE_FLOAT)
+ return 0;
+
+ return c4x_immed_float_p (op);
+
+#if Pmode != QImode
+ case Pmode:
+#endif
+ case QImode:
+ if (GET_CODE (op) == CONSTANT_P_RTX)
+ return 1;
+
+ if (GET_CODE (op) != CONST_INT
+ || (GET_MODE (op) != VOIDmode && GET_MODE (op) != mode)
+ || GET_MODE_CLASS (mode) != MODE_INT)
+ return 0;
+
+ return IS_HIGH_CONST (INTVAL (op)) || IS_INT16_CONST (INTVAL (op));
+
+ case HImode:
+ return 0;
+
+ default:
+ return 0;
+ }
+}
+
+
+int
+stik_const_operand (op, mode)
+ rtx op;
+ enum machine_mode mode ATTRIBUTE_UNUSED;
+{
+ return c4x_K_constant (op);
+}
+
+
+int
+not_const_operand (op, mode)
+ rtx op;
+ enum machine_mode mode ATTRIBUTE_UNUSED;
+{
+ return c4x_N_constant (op);
+}
+
+
+int
+reg_operand (op, mode)
+ rtx op;
+ enum machine_mode mode;
+{
+ if (GET_CODE (op) == SUBREG
+ && GET_MODE (op) == QFmode)
+ return 0;
+ return register_operand (op, mode);
+}
+
+
+int
+mixed_subreg_operand (op, mode)
+ rtx op;
+ enum machine_mode mode;
+{
+ /* Allow (subreg:HF (reg:HI)) that be generated for a union of an
+ int and a long double. */
+ if (GET_CODE (op) == SUBREG
+ && (GET_MODE (op) == QFmode)
+ && (GET_MODE (SUBREG_REG (op)) == QImode
+ || GET_MODE (SUBREG_REG (op)) == HImode))
+ return 1;
+ return 0;
+}
+
+
+int
+reg_imm_operand (op, mode)
+ rtx op;
+ enum machine_mode mode ATTRIBUTE_UNUSED;
+{
+ if (REG_P (op) || CONSTANT_P (op))
+ return 1;
+ return 0;
+}
+
+
+int
+not_modify_reg (op, mode)
+ rtx op;
+ enum machine_mode mode ATTRIBUTE_UNUSED;
+{
+ if (REG_P (op) || CONSTANT_P (op))
+ return 1;
+ if (GET_CODE (op) != MEM)
+ return 0;
+ op = XEXP (op, 0);
+ switch (GET_CODE (op))
+ {
+ case REG:
+ return 1;
+
+ case PLUS:
+ {
+ rtx op0 = XEXP (op, 0);
+ rtx op1 = XEXP (op, 1);
+
+ if (! REG_P (op0))
+ return 0;
+
+ if (REG_P (op1) || GET_CODE (op1) == CONST_INT)
+ return 1;
+ }
+
+ case LO_SUM:
+ {
+ rtx op0 = XEXP (op, 0);
+
+ if (REG_P (op0) && REGNO (op0) == DP_REGNO)
+ return 1;
+ }
+ break;
+
+ case CONST:
+ case SYMBOL_REF:
+ case LABEL_REF:
+ return 1;
+ default:
+ break;
+ }
+ return 0;
+}
+
+
+int
+not_rc_reg (op, mode)
+ rtx op;
+ enum machine_mode mode ATTRIBUTE_UNUSED;
+{
+ if (REG_P (op) && REGNO (op) == RC_REGNO)
+ return 0;
+ return 1;
+}
+
+
+/* Extended precision register R0-R1. */
+
+int
+r0r1_reg_operand (op, mode)
+ rtx op;
+ enum machine_mode mode;
+{
+ if (! reg_operand (op, mode))
+ return 0;
+ if (GET_CODE (op) == SUBREG)
+ op = SUBREG_REG (op);
+ return REG_P (op) && IS_R0R1_OR_PSEUDO_REGNO (op);
+}
+
+
+/* Extended precision register R2-R3. */
+
+int
+r2r3_reg_operand (op, mode)
+ rtx op;
+ enum machine_mode mode;
+{
+ if (! reg_operand (op, mode))
+ return 0;
+ if (GET_CODE (op) == SUBREG)
+ op = SUBREG_REG (op);
+ return REG_P (op) && IS_R2R3_OR_PSEUDO_REGNO (op);
+}
+
+
+/* Low extended precision register R0-R7. */
+
+int
+ext_low_reg_operand (op, mode)
+ rtx op;
+ enum machine_mode mode;
+{
+ if (! reg_operand (op, mode))
+ return 0;
+ if (GET_CODE (op) == SUBREG)
+ op = SUBREG_REG (op);
+ return REG_P (op) && IS_EXT_LOW_OR_PSEUDO_REGNO (op);
+}
+
+
+/* Extended precision register. */
+
+int
+ext_reg_operand (op, mode)
+ rtx op;
+ enum machine_mode mode;
+{
+ if (! reg_operand (op, mode))
+ return 0;
+ if (GET_CODE (op) == SUBREG)
+ op = SUBREG_REG (op);
+ if (! REG_P (op))
+ return 0;
+ return IS_EXT_OR_PSEUDO_REGNO (op);
+}
+
+
+/* Standard precision register. */
+
+int
+std_reg_operand (op, mode)
+ rtx op;
+ enum machine_mode mode;
+{
+ if (! reg_operand (op, mode))
+ return 0;
+ if (GET_CODE (op) == SUBREG)
+ op = SUBREG_REG (op);
+ return REG_P (op) && IS_STD_OR_PSEUDO_REGNO (op);
+}
+
+
+/* Address register. */
+
+int
+addr_reg_operand (op, mode)
+ rtx op;
+ enum machine_mode mode;
+{
+ if (! reg_operand (op, mode))
+ return 0;
+ return c4x_a_register (op);
+}
+
+
+/* Index register. */
+
+int
+index_reg_operand (op, mode)
+ rtx op;
+ enum machine_mode mode;
+{
+ if (! reg_operand (op, mode))
+ return 0;
+ if (GET_CODE (op) == SUBREG)
+ op = SUBREG_REG (op);
+ return c4x_x_register (op);
+}
+
+
+/* DP register. */
+
+int
+dp_reg_operand (op, mode)
+ rtx op;
+ enum machine_mode mode ATTRIBUTE_UNUSED;
+{
+ return REG_P (op) && IS_DP_OR_PSEUDO_REGNO (op);
+}
+
+
+/* SP register. */
+
+int
+sp_reg_operand (op, mode)
+ rtx op;
+ enum machine_mode mode ATTRIBUTE_UNUSED;
+{
+ return REG_P (op) && IS_SP_OR_PSEUDO_REGNO (op);
+}
+
+
+/* ST register. */
+
+int
+st_reg_operand (op, mode)
+ register rtx op;
+ enum machine_mode mode ATTRIBUTE_UNUSED;
+{
+ return REG_P (op) && IS_ST_OR_PSEUDO_REGNO (op);
+}
+
+
+/* RC register. */
+
+int
+rc_reg_operand (op, mode)
+ register rtx op;
+ enum machine_mode mode ATTRIBUTE_UNUSED;
+{
+ return REG_P (op) && IS_RC_OR_PSEUDO_REGNO (op);
+}
+
+
+int
+call_address_operand (op, mode)
+ rtx op;
+ enum machine_mode mode ATTRIBUTE_UNUSED;
+{
+ return (REG_P (op) || symbolic_address_operand (op, mode));
+}
+
+
+/* Symbolic address operand. */
+
+int
+symbolic_address_operand (op, mode)
+ register rtx op;
+ enum machine_mode mode ATTRIBUTE_UNUSED;
+{
+ switch (GET_CODE (op))
+ {
+ case SYMBOL_REF:
+ case LABEL_REF:
+ return 1;
+ case CONST:
+ op = XEXP (op, 0);
+ return ((GET_CODE (XEXP (op, 0)) == SYMBOL_REF
+ || GET_CODE (XEXP (op, 0)) == LABEL_REF)
+ && GET_CODE (XEXP (op, 1)) == CONST_INT);
+ default:
+ return 0;
+ }
+}
+
+
+/* Check src operand of two operand arithmetic instructions. */
+
+int
+src_operand (op, mode)
+ rtx op;
+ enum machine_mode mode;
+{
+ if (GET_CODE (op) == SUBREG
+ && mixed_subreg_operand (op, mode))
+ return 0;
+
+ if (REG_P (op))
+ return reg_operand (op, mode);
+
+ if (mode == VOIDmode)
+ mode = GET_MODE (op);
+
+ if (GET_CODE (op) == CONST_INT)
+ return (mode == QImode || mode == Pmode || mode == HImode)
+ && c4x_I_constant (op);
+
+ /* We don't like CONST_DOUBLE integers. */
+ if (GET_CODE (op) == CONST_DOUBLE)
+ return c4x_H_constant (op);
+
+ /* Disallow symbolic addresses. */
+ if (GET_CODE (op) == SYMBOL_REF
+ || GET_CODE (op) == LABEL_REF
+ || GET_CODE (op) == CONST)
+ return 0;
+
+ /* Disallow direct memory access symbolic addresses.
+ These are usually caught by the movqi expander and
+ converted to a LO_SUM. */
+ if (GET_CODE (op) == MEM
+ && ((GET_CODE (XEXP (op, 0)) == SYMBOL_REF
+ || GET_CODE (XEXP (op, 0)) == LABEL_REF
+ || GET_CODE (XEXP (op, 0)) == CONST)))
+ return 0;
+
+ return general_operand (op, mode);
+}
+
+
+int
+src_hi_operand (op, mode)
+ rtx op;
+ enum machine_mode mode;
+{
+ if (c4x_O_constant (op))
+ return 1;
+ return src_operand (op, mode);
+}
+
+
+/* Check src operand of two operand logical instructions. */
+
+int
+lsrc_operand (op, mode)
+ rtx op;
+ enum machine_mode mode;
+{
+ if (mode == VOIDmode)
+ mode = GET_MODE (op);
+
+ if (mode != QImode && mode != Pmode)
+ fatal_insn ("Mode not QImode", op);
+
+ if (GET_CODE (op) == CONST_INT)
+ return c4x_L_constant (op) || c4x_J_constant (op);
+
+ return src_operand (op, mode);
+}
+
+
+/* Check src operand of two operand tricky instructions. */
+
+int
+tsrc_operand (op, mode)
+ rtx op;
+ enum machine_mode mode;
+{
+ if (mode == VOIDmode)
+ mode = GET_MODE (op);
+
+ if (mode != QImode && mode != Pmode)
+ fatal_insn ("Mode not QImode", op);
+
+ if (GET_CODE (op) == CONST_INT)
+ return c4x_L_constant (op) || c4x_N_constant (op) || c4x_J_constant (op);
+
+ return src_operand (op, mode);
+}
+
+
+int
+reg_or_const_operand (op, mode)
+ rtx op;
+ enum machine_mode mode;
+{
+ return reg_operand (op, mode) || const_operand (op, mode);
+}
+
+
+/* Check for indirect operands allowable in parallel instruction. */
+
+int
+par_ind_operand (op, mode)
+ rtx op;
+ enum machine_mode mode;
+{
+ if (mode != VOIDmode && mode != GET_MODE (op))
+ return 0;
+
+ return c4x_S_indirect (op);
+}
+
+
+/* Check for operands allowable in parallel instruction. */
+
+int
+parallel_operand (op, mode)
+ rtx op;
+ enum machine_mode mode;
+{
+ return ext_low_reg_operand (op, mode) || par_ind_operand (op, mode);
+}
+
+
+static void
+c4x_S_address_parse (op, base, incdec, index, disp)
+ rtx op;
+ int *base;
+ int *incdec;
+ int *index;
+ int *disp;
+{
+ *base = 0;
+ *incdec = 0;
+ *index = 0;
+ *disp = 0;
+
+ if (GET_CODE (op) != MEM)
+ fatal_insn ("Invalid indirect memory address", op);
+
+ op = XEXP (op, 0);
+ switch (GET_CODE (op))
+ {
+ case PRE_DEC:
+ *base = REGNO (XEXP (op, 0));
+ *incdec = 1;
+ *disp = -1;
+ return;
+
+ case POST_DEC:
+ *base = REGNO (XEXP (op, 0));
+ *incdec = 1;
+ *disp = 0;
+ return;
+
+ case PRE_INC:
+ *base = REGNO (XEXP (op, 0));
+ *incdec = 1;
+ *disp = 1;
+ return;
+
+ case POST_INC:
+ *base = REGNO (XEXP (op, 0));
+ *incdec = 1;
+ *disp = 0;
+ return;
+
+ case POST_MODIFY:
+ *base = REGNO (XEXP (op, 0));
+ if (REG_P (XEXP (XEXP (op, 1), 1)))
+ {
+ *index = REGNO (XEXP (XEXP (op, 1), 1));
+ *disp = 0; /* ??? */
+ }
+ else
+ *disp = INTVAL (XEXP (XEXP (op, 1), 1));
+ *incdec = 1;
+ return;
+
+ case PRE_MODIFY:
+ *base = REGNO (XEXP (op, 0));
+ if (REG_P (XEXP (XEXP (op, 1), 1)))
+ {
+ *index = REGNO (XEXP (XEXP (op, 1), 1));
+ *disp = 1; /* ??? */
+ }
+ else
+ *disp = INTVAL (XEXP (XEXP (op, 1), 1));
+ *incdec = 1;
+
+ return;
+
+ case REG:
+ *base = REGNO (op);
+ return;
+
+ case PLUS:
+ {
+ rtx op0 = XEXP (op, 0);
+ rtx op1 = XEXP (op, 1);
+
+ if (c4x_a_register (op0))
+ {
+ if (c4x_x_register (op1))
+ {
+ *base = REGNO (op0);
+ *index = REGNO (op1);
+ return;
+ }
+ else if ((GET_CODE (op1) == CONST_INT
+ && IS_DISP1_CONST (INTVAL (op1))))
+ {
+ *base = REGNO (op0);
+ *disp = INTVAL (op1);
+ return;
+ }
+ }
+ else if (c4x_x_register (op0) && c4x_a_register (op1))
+ {
+ *base = REGNO (op1);
+ *index = REGNO (op0);
+ return;
+ }
+ }
+ /* Fallthrough */
+
+ default:
+ fatal_insn ("Invalid indirect (S) memory address", op);
+ }
+}
+
+
+int
+c4x_address_conflict (op0, op1, store0, store1)
+ rtx op0;
+ rtx op1;
+ int store0;
+ int store1;
+{
+ int base0;
+ int base1;
+ int incdec0;
+ int incdec1;
+ int index0;
+ int index1;
+ int disp0;
+ int disp1;
+
+ if (MEM_VOLATILE_P (op0) && MEM_VOLATILE_P (op1))
+ return 1;
+
+ c4x_S_address_parse (op0, &base0, &incdec0, &index0, &disp0);
+ c4x_S_address_parse (op1, &base1, &incdec1, &index1, &disp1);
+
+ if (store0 && store1)
+ {
+ /* If we have two stores in parallel to the same address, then
+ the C4x only executes one of the stores. This is unlikely to
+ cause problems except when writing to a hardware device such
+ as a FIFO since the second write will be lost. The user
+ should flag the hardware location as being volatile so that
+ we don't do this optimisation. While it is unlikely that we
+ have an aliased address if both locations are not marked
+ volatile, it is probably safer to flag a potential conflict
+ if either location is volatile. */
+ if (! flag_argument_noalias)
+ {
+ if (MEM_VOLATILE_P (op0) || MEM_VOLATILE_P (op1))
+ return 1;
+ }
+ }
+
+ /* If have a parallel load and a store to the same address, the load
+ is performed first, so there is no conflict. Similarly, there is
+ no conflict if have parallel loads from the same address. */
+
+ /* Cannot use auto increment or auto decrement twice for same
+ base register. */
+ if (base0 == base1 && incdec0 && incdec0)
+ return 1;
+
+ /* It might be too confusing for GCC if we have use a base register
+ with a side effect and a memory reference using the same register
+ in parallel. */
+ if (! TARGET_DEVEL && base0 == base1 && (incdec0 || incdec1))
+ return 1;
+
+ /* We can not optimize the case where op1 and op2 refer to the same
+ address. */
+ if (base0 == base1 && disp0 == disp1 && index0 == index1)
+ return 1;
+
+ /* No conflict. */
+ return 0;
+}
+
+
+/* Check for while loop inside a decrement and branch loop. */
+
+int
+c4x_label_conflict (insn, jump, db)
+ rtx insn;
+ rtx jump;
+ rtx db;
+{
+ while (insn)
+ {
+ if (GET_CODE (insn) == CODE_LABEL)
+ {
+ if (CODE_LABEL_NUMBER (jump) == CODE_LABEL_NUMBER (insn))
+ return 1;
+ if (CODE_LABEL_NUMBER (db) == CODE_LABEL_NUMBER (insn))
+ return 0;
+ }
+ insn = PREV_INSN (insn);
+ }
+ return 1;
+}
+
+
+/* Validate combination of operands for parallel load/store instructions. */
+
+int
+valid_parallel_load_store (operands, mode)
+ rtx *operands;
+ enum machine_mode mode ATTRIBUTE_UNUSED;
+{
+ rtx op0 = operands[0];
+ rtx op1 = operands[1];
+ rtx op2 = operands[2];
+ rtx op3 = operands[3];
+
+ if (GET_CODE (op0) == SUBREG)
+ op0 = SUBREG_REG (op0);
+ if (GET_CODE (op1) == SUBREG)
+ op1 = SUBREG_REG (op1);
+ if (GET_CODE (op2) == SUBREG)
+ op2 = SUBREG_REG (op2);
+ if (GET_CODE (op3) == SUBREG)
+ op3 = SUBREG_REG (op3);
+
+ /* The patterns should only allow ext_low_reg_operand() or
+ par_ind_operand() operands. Thus of the 4 operands, only 2
+ should be REGs and the other 2 should be MEMs. */
+
+ /* This test prevents the multipack pass from using this pattern if
+ op0 is used as an index or base register in op2 or op3, since
+ this combination will require reloading. */
+ if (GET_CODE (op0) == REG
+ && ((GET_CODE (op2) == MEM && reg_mentioned_p (op0, XEXP (op2, 0)))
+ || (GET_CODE (op3) == MEM && reg_mentioned_p (op0, XEXP (op3, 0)))))
+ return 0;
+
+ /* LDI||LDI */
+ if (GET_CODE (op0) == REG && GET_CODE (op2) == REG)
+ return (REGNO (op0) != REGNO (op2))
+ && GET_CODE (op1) == MEM && GET_CODE (op3) == MEM
+ && ! c4x_address_conflict (op1, op3, 0, 0);
+
+ /* STI||STI */
+ if (GET_CODE (op1) == REG && GET_CODE (op3) == REG)
+ return GET_CODE (op0) == MEM && GET_CODE (op2) == MEM
+ && ! c4x_address_conflict (op0, op2, 1, 1);
+
+ /* LDI||STI */
+ if (GET_CODE (op0) == REG && GET_CODE (op3) == REG)
+ return GET_CODE (op1) == MEM && GET_CODE (op2) == MEM
+ && ! c4x_address_conflict (op1, op2, 0, 1);
+
+ /* STI||LDI */
+ if (GET_CODE (op1) == REG && GET_CODE (op2) == REG)
+ return GET_CODE (op0) == MEM && GET_CODE (op3) == MEM
+ && ! c4x_address_conflict (op0, op3, 1, 0);
+
+ return 0;
+}
+
+
+int
+valid_parallel_operands_4 (operands, mode)
+ rtx *operands;
+ enum machine_mode mode ATTRIBUTE_UNUSED;
+{
+ rtx op0 = operands[0];
+ rtx op2 = operands[2];
+
+ if (GET_CODE (op0) == SUBREG)
+ op0 = SUBREG_REG (op0);
+ if (GET_CODE (op2) == SUBREG)
+ op2 = SUBREG_REG (op2);
+
+ /* This test prevents the multipack pass from using this pattern if
+ op0 is used as an index or base register in op2, since this combination
+ will require reloading. */
+ if (GET_CODE (op0) == REG
+ && GET_CODE (op2) == MEM
+ && reg_mentioned_p (op0, XEXP (op2, 0)))
+ return 0;
+
+ return 1;
+}
+
+
+int
+valid_parallel_operands_5 (operands, mode)
+ rtx *operands;
+ enum machine_mode mode ATTRIBUTE_UNUSED;
+{
+ int regs = 0;
+ rtx op0 = operands[0];
+ rtx op1 = operands[1];
+ rtx op2 = operands[2];
+ rtx op3 = operands[3];
+
+ if (GET_CODE (op0) == SUBREG)
+ op0 = SUBREG_REG (op0);
+ if (GET_CODE (op1) == SUBREG)
+ op1 = SUBREG_REG (op1);
+ if (GET_CODE (op2) == SUBREG)
+ op2 = SUBREG_REG (op2);
+
+ /* The patterns should only allow ext_low_reg_operand() or
+ par_ind_operand() operands. Operands 1 and 2 may be commutative
+ but only one of them can be a register. */
+ if (GET_CODE (op1) == REG)
+ regs++;
+ if (GET_CODE (op2) == REG)
+ regs++;
+
+ if (regs != 1)
+ return 0;
+
+ /* This test prevents the multipack pass from using this pattern if
+ op0 is used as an index or base register in op3, since this combination
+ will require reloading. */
+ if (GET_CODE (op0) == REG
+ && GET_CODE (op3) == MEM
+ && reg_mentioned_p (op0, XEXP (op3, 0)))
+ return 0;
+
+ return 1;
+}
+
+
+int
+valid_parallel_operands_6 (operands, mode)
+ rtx *operands;
+ enum machine_mode mode ATTRIBUTE_UNUSED;
+{
+ int regs = 0;
+ rtx op0 = operands[0];
+ rtx op1 = operands[1];
+ rtx op2 = operands[2];
+ rtx op4 = operands[4];
+ rtx op5 = operands[5];
+
+ if (GET_CODE (op1) == SUBREG)
+ op1 = SUBREG_REG (op1);
+ if (GET_CODE (op2) == SUBREG)
+ op2 = SUBREG_REG (op2);
+ if (GET_CODE (op4) == SUBREG)
+ op4 = SUBREG_REG (op4);
+ if (GET_CODE (op5) == SUBREG)
+ op5 = SUBREG_REG (op5);
+
+ /* The patterns should only allow ext_low_reg_operand() or
+ par_ind_operand() operands. Thus of the 4 input operands, only 2
+ should be REGs and the other 2 should be MEMs. */
+
+ if (GET_CODE (op1) == REG)
+ regs++;
+ if (GET_CODE (op2) == REG)
+ regs++;
+ if (GET_CODE (op4) == REG)
+ regs++;
+ if (GET_CODE (op5) == REG)
+ regs++;
+
+ /* The new C30/C40 silicon dies allow 3 regs of the 4 input operands.
+ Perhaps we should count the MEMs as well? */
+ if (regs != 2)
+ return 0;
+
+ /* This test prevents the multipack pass from using this pattern if
+ op0 is used as an index or base register in op4 or op5, since
+ this combination will require reloading. */
+ if (GET_CODE (op0) == REG
+ && ((GET_CODE (op4) == MEM && reg_mentioned_p (op0, XEXP (op4, 0)))
+ || (GET_CODE (op5) == MEM && reg_mentioned_p (op0, XEXP (op5, 0)))))
+ return 0;
+
+ return 1;
+}
+
+
+/* Validate combination of src operands. Note that the operands have
+ been screened by the src_operand predicate. We just have to check
+ that the combination of operands is valid. If FORCE is set, ensure
+ that the destination regno is valid if we have a 2 operand insn. */
+
+static int
+c4x_valid_operands (code, operands, mode, force)
+ enum rtx_code code;
+ rtx *operands;
+ enum machine_mode mode;
+ int force;
+{
+ rtx op1;
+ rtx op2;
+ enum rtx_code code1;
+ enum rtx_code code2;
+
+ if (code == COMPARE)
+ {
+ op1 = operands[0];
+ op2 = operands[1];
+ }
+ else
+ {
+ op1 = operands[1];
+ op2 = operands[2];
+ }
+
+ if (GET_CODE (op1) == SUBREG)
+ op1 = SUBREG_REG (op1);
+ if (GET_CODE (op2) == SUBREG)
+ op2 = SUBREG_REG (op2);
+
+ code1 = GET_CODE (op1);
+ code2 = GET_CODE (op2);
+
+ if (code1 == REG && code2 == REG)
+ return 1;
+
+ if (code1 == MEM && code2 == MEM)
+ {
+ if (c4x_S_indirect (op1, mode) && c4x_S_indirect (op2, mode))
+ return 1;
+ return c4x_R_indirect (op1, mode) && c4x_R_indirect (op2, mode);
+ }
+
+ if (code1 == code2)
+ return 0;
+
+ if (code1 == REG)
+ {
+ switch (code2)
+ {
+ case CONST_INT:
+ if (c4x_J_constant (op2) && c4x_R_indirect (op1))
+ return 1;
+ break;
+
+ case CONST_DOUBLE:
+ if (! c4x_H_constant (op2))
+ return 0;
+ break;
+
+ /* Any valid memory operand screened by src_operand is OK. */
+ case MEM:
+
+ /* After CSE, any remaining (ADDRESSOF:P reg) gets converted
+ into a stack slot memory address comprising a PLUS and a
+ constant. */
+ case ADDRESSOF:
+ break;
+
+ default:
+ fatal_insn ("c4x_valid_operands: Internal error", op2);
+ break;
+ }
+
+ /* Check that we have a valid destination register for a two operand
+ instruction. */
+ return ! force || code == COMPARE || REGNO (op1) == REGNO (operands[0]);
+ }
+
+ /* We assume MINUS is commutative since the subtract patterns
+ also support the reverse subtract instructions. Since op1
+ is not a register, and op2 is a register, op1 can only
+ be a restricted memory operand for a shift instruction. */
+ if (code == ASHIFTRT || code == LSHIFTRT
+ || code == ASHIFT || code == COMPARE)
+ return code2 == REG
+ && (c4x_S_indirect (op1) || c4x_R_indirect (op1));
+
+ switch (code1)
+ {
+ case CONST_INT:
+ if (c4x_J_constant (op1) && c4x_R_indirect (op2))
+ return 1;
+ break;
+
+ case CONST_DOUBLE:
+ if (! c4x_H_constant (op1))
+ return 0;
+ break;
+
+ /* Any valid memory operand screened by src_operand is OK. */
+ case MEM:
+#if 0
+ if (code2 != REG)
+ return 0;
+#endif
+ break;
+
+ /* After CSE, any remaining (ADDRESSOF:P reg) gets converted
+ into a stack slot memory address comprising a PLUS and a
+ constant. */
+ case ADDRESSOF:
+ break;
+
+ default:
+ fatal ("c4x_valid_operands: Internal error");
+ break;
+ }
+
+ /* Check that we have a valid destination register for a two operand
+ instruction. */
+ return ! force || REGNO (op1) == REGNO (operands[0]);
+}
+
+
+int valid_operands (code, operands, mode)
+ enum rtx_code code;
+ rtx *operands;
+ enum machine_mode mode;
+{
+
+ /* If we are not optimizing then we have to let anything go and let
+ reload fix things up. instantiate_decl in function.c can produce
+ invalid insns by changing the offset of a memory operand from a
+ valid one into an invalid one, when the second operand is also a
+ memory operand. The alternative is not to allow two memory
+ operands for an insn when not optimizing. The problem only rarely
+ occurs, for example with the C-torture program DFcmp.c */
+
+ return ! optimize || c4x_valid_operands (code, operands, mode, 0);
+}
+
+
+int
+legitimize_operands (code, operands, mode)
+ enum rtx_code code;
+ rtx *operands;
+ enum machine_mode mode;
+{
+ /* Compare only has 2 operands. */
+ if (code == COMPARE)
+ {
+ /* During RTL generation, force constants into pseudos so that
+ they can get hoisted out of loops. This will tie up an extra
+ register but can save an extra cycle. Only do this if loop
+ optimisation enabled. (We cannot pull this trick for add and
+ sub instructions since the flow pass won't find
+ autoincrements etc.) This allows us to generate compare
+ instructions like CMPI R0, *AR0++ where R0 = 42, say, instead
+ of LDI *AR0++, R0; CMPI 42, R0.
+
+ Note that expand_binops will try to load an expensive constant
+ into a register if it is used within a loop. Unfortunately,
+ the cost mechanism doesn't allow us to look at the other
+ operand to decide whether the constant is expensive. */
+
+ if (! reload_in_progress
+ && TARGET_HOIST
+ && optimize > 0
+ && GET_CODE (operands[1]) == CONST_INT
+ && preserve_subexpressions_p ()
+ && rtx_cost (operands[1], code) > 1)
+ operands[1] = force_reg (mode, operands[1]);
+
+ if (! reload_in_progress
+ && ! c4x_valid_operands (code, operands, mode, 0))
+ operands[0] = force_reg (mode, operands[0]);
+ return 1;
+ }
+
+ /* We cannot do this for ADDI/SUBI insns since we will
+ defeat the flow pass from finding autoincrement addressing
+ opportunities. */
+ if (! reload_in_progress
+ && ! ((code == PLUS || code == MINUS) && mode == Pmode)
+ && TARGET_HOIST
+ && optimize > 1
+ && GET_CODE (operands[2]) == CONST_INT
+ && preserve_subexpressions_p ()
+ && rtx_cost (operands[2], code) > 1)
+ operands[2] = force_reg (mode, operands[2]);
+
+ /* We can get better code on a C30 if we force constant shift counts
+ into a register. This way they can get hoisted out of loops,
+ tying up a register, but saving an instruction. The downside is
+ that they may get allocated to an address or index register, and
+ thus we will get a pipeline conflict if there is a nearby
+ indirect address using an address register.
+
+ Note that expand_binops will not try to load an expensive constant
+ into a register if it is used within a loop for a shift insn. */
+
+ if (! reload_in_progress
+ && ! c4x_valid_operands (code, operands, mode, TARGET_FORCE))
+ {
+ /* If the operand combination is invalid, we force operand1 into a
+ register, preventing reload from having doing to do this at a
+ later stage. */
+ operands[1] = force_reg (mode, operands[1]);
+ if (TARGET_FORCE)
+ {
+ emit_move_insn (operands[0], operands[1]);
+ operands[1] = copy_rtx (operands[0]);
+ }
+ else
+ {
+ /* Just in case... */
+ if (! c4x_valid_operands (code, operands, mode, 0))
+ operands[2] = force_reg (mode, operands[2]);
+ }
+ }
+
+ /* Right shifts require a negative shift count, but GCC expects
+ a positive count, so we emit a NEG. */
+ if ((code == ASHIFTRT || code == LSHIFTRT)
+ && (GET_CODE (operands[2]) != CONST_INT))
+ operands[2] = gen_rtx_NEG (mode, negate_rtx (mode, operands[2]));
+
+ return 1;
+}
+
+
+/* The following predicates are used for instruction scheduling. */
+
+int
+group1_reg_operand (op, mode)
+ rtx op;
+ enum machine_mode mode;
+{
+ if (mode != VOIDmode && mode != GET_MODE (op))
+ return 0;
+ if (GET_CODE (op) == SUBREG)
+ op = SUBREG_REG (op);
+ return REG_P (op) && IS_GROUP1_REG (REGNO (op));
+}
+
+
+int
+group1_mem_operand (op, mode)
+ rtx op;
+ enum machine_mode mode;
+{
+ if (mode != VOIDmode && mode != GET_MODE (op))
+ return 0;
+
+ if (GET_CODE (op) == MEM)
+ {
+ op = XEXP (op, 0);
+ if (GET_CODE (op) == PLUS)
+ {
+ rtx op0 = XEXP (op, 0);
+ rtx op1 = XEXP (op, 1);
+
+ if (((GET_CODE (op0) == REG) && IS_GROUP1_REGNO (op0))
+ || ((GET_CODE (op1) == REG) && IS_GROUP1_REGNO (op1)))
+ return 1;
+ }
+ else if ((REG_P (op)) && IS_GROUP1_REGNO (op))
+ return 1;
+ }
+
+ return 0;
+}
+
+
+/* Return true if any one of the address registers. */
+
+int
+arx_reg_operand (op, mode)
+ rtx op;
+ enum machine_mode mode;
+{
+ if (mode != VOIDmode && mode != GET_MODE (op))
+ return 0;
+ if (GET_CODE (op) == SUBREG)
+ op = SUBREG_REG (op);
+ return REG_P (op) && IS_ADDR_REGNO (op);
+}
+
+
+static int
+c4x_arn_reg_operand (op, mode, regno)
+ rtx op;
+ enum machine_mode mode;
+ int regno;
+{
+ if (mode != VOIDmode && mode != GET_MODE (op))
+ return 0;
+ if (GET_CODE (op) == SUBREG)
+ op = SUBREG_REG (op);
+ return REG_P (op) && (REGNO (op) == regno);
+}
+
+
+static int
+c4x_arn_mem_operand (op, mode, regno)
+ rtx op;
+ enum machine_mode mode;
+ int regno;
+{
+ if (mode != VOIDmode && mode != GET_MODE (op))
+ return 0;
+
+ if (GET_CODE (op) == MEM)
+ {
+ op = XEXP (op, 0);
+ switch (GET_CODE (op))
+ {
+ case PRE_DEC:
+ case POST_DEC:
+ case PRE_INC:
+ case POST_INC:
+ op = XEXP (op, 0);
+
+ case REG:
+ if (REG_P (op) && (REGNO (op) == regno))
+ return 1;
+ break;
+
+ case PRE_MODIFY:
+ case POST_MODIFY:
+ if (REG_P (XEXP (op, 0)) && (REGNO (XEXP (op, 0)) == regno))
+ return 1;
+ if (REG_P (XEXP (XEXP (op, 1), 1))
+ && (REGNO (XEXP (XEXP (op, 1), 1)) == regno))
+ return 1;
+ break;
+
+ case PLUS:
+ {
+ rtx op0 = XEXP (op, 0);
+ rtx op1 = XEXP (op, 1);
+
+ if (((GET_CODE (op0) == REG) && (REGNO (op0) == regno))
+ || ((GET_CODE (op1) == REG) && (REGNO (op1) == regno)))
+ return 1;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ return 0;
+}
+
+
+int
+ar0_reg_operand (op, mode)
+ rtx op;
+ enum machine_mode mode;
+{
+ return c4x_arn_reg_operand (op, mode, AR0_REGNO);
+}
+
+
+int
+ar0_mem_operand (op, mode)
+ rtx op;
+ enum machine_mode mode;
+{
+ return c4x_arn_mem_operand (op, mode, AR0_REGNO);
+}
+
+
+int
+ar1_reg_operand (op, mode)
+ rtx op;
+ enum machine_mode mode;
+{
+ return c4x_arn_reg_operand (op, mode, AR1_REGNO);
+}
+
+
+int
+ar1_mem_operand (op, mode)
+ rtx op;
+ enum machine_mode mode;
+{
+ return c4x_arn_mem_operand (op, mode, AR1_REGNO);
+}
+
+
+int
+ar2_reg_operand (op, mode)
+ rtx op;
+ enum machine_mode mode;
+{
+ return c4x_arn_reg_operand (op, mode, AR2_REGNO);
+}
+
+
+int
+ar2_mem_operand (op, mode)
+ rtx op;
+ enum machine_mode mode;
+{
+ return c4x_arn_mem_operand (op, mode, AR2_REGNO);
+}
+
+
+int
+ar3_reg_operand (op, mode)
+ rtx op;
+ enum machine_mode mode;
+{
+ return c4x_arn_reg_operand (op, mode, AR3_REGNO);
+}
+
+
+int
+ar3_mem_operand (op, mode)
+ rtx op;
+ enum machine_mode mode;
+{
+ return c4x_arn_mem_operand (op, mode, AR3_REGNO);
+}
+
+
+int
+ar4_reg_operand (op, mode)
+ rtx op;
+ enum machine_mode mode;
+{
+ return c4x_arn_reg_operand (op, mode, AR4_REGNO);
+}
+
+
+int
+ar4_mem_operand (op, mode)
+ rtx op;
+ enum machine_mode mode;
+{
+ return c4x_arn_mem_operand (op, mode, AR4_REGNO);
+}
+
+
+int
+ar5_reg_operand (op, mode)
+ rtx op;
+ enum machine_mode mode;
+{
+ return c4x_arn_reg_operand (op, mode, AR5_REGNO);
+}
+
+
+int
+ar5_mem_operand (op, mode)
+ rtx op;
+ enum machine_mode mode;
+{
+ return c4x_arn_mem_operand (op, mode, AR5_REGNO);
+}
+
+
+int
+ar6_reg_operand (op, mode)
+ rtx op;
+ enum machine_mode mode;
+{
+ return c4x_arn_reg_operand (op, mode, AR6_REGNO);
+}
+
+
+int
+ar6_mem_operand (op, mode)
+ rtx op;
+ enum machine_mode mode;
+{
+ return c4x_arn_mem_operand (op, mode, AR6_REGNO);
+}
+
+
+int
+ar7_reg_operand (op, mode)
+ rtx op;
+ enum machine_mode mode;
+{
+ return c4x_arn_reg_operand (op, mode, AR7_REGNO);
+}
+
+
+int
+ar7_mem_operand (op, mode)
+ rtx op;
+ enum machine_mode mode;
+{
+ return c4x_arn_mem_operand (op, mode, AR7_REGNO);
+}
+
+
+int
+ir0_reg_operand (op, mode)
+ rtx op;
+ enum machine_mode mode;
+{
+ return c4x_arn_reg_operand (op, mode, IR0_REGNO);
+}
+
+
+int
+ir0_mem_operand (op, mode)
+ rtx op;
+ enum machine_mode mode;
+{
+ return c4x_arn_mem_operand (op, mode, IR0_REGNO);
+}
+
+
+int
+ir1_reg_operand (op, mode)
+ rtx op;
+ enum machine_mode mode;
+{
+ return c4x_arn_reg_operand (op, mode, IR1_REGNO);
+}
+
+
+int
+ir1_mem_operand (op, mode)
+ rtx op;
+ enum machine_mode mode;
+{
+ return c4x_arn_mem_operand (op, mode, IR1_REGNO);
+}
+
+
+/* We allow autoincrement addressing. */
+
+rtx
+c4x_operand_subword (op, i, validate_address, mode)
+ rtx op;
+ int i;
+ int validate_address;
+ enum machine_mode mode;
+{
+ if (mode != HImode && mode != HFmode)
+ fatal_insn ("c4x_operand_subword: invalid mode", op);
+
+ if (mode == HFmode && REG_P (op))
+ fatal_insn ("c4x_operand_subword: invalid operand", op);
+
+ if (GET_CODE (op) == MEM)
+ {
+ enum rtx_code code = GET_CODE (XEXP (op, 0));
+ enum machine_mode mode = GET_MODE (XEXP (op, 0));
+ enum machine_mode submode;
+
+ submode = mode;
+ if (mode == HImode)
+ submode = QImode;
+ else if (mode == HFmode)
+ submode = QFmode;
+
+ switch (code)
+ {
+ case POST_INC:
+ case PRE_INC:
+ return gen_rtx_MEM (submode, XEXP (op, 0));
+
+ case POST_DEC:
+ case PRE_DEC:
+ case PRE_MODIFY:
+ case POST_MODIFY:
+ /* We could handle these with some difficulty.
+ e.g., *p-- => *(p-=2); *(p+1). */
+ fatal_insn ("c4x_operand_subword: invalid autoincrement", op);
+
+ case SYMBOL_REF:
+ case LABEL_REF:
+ case CONST:
+ case CONST_INT:
+ fatal_insn ("c4x_operand_subword: invalid address", op);
+
+ /* Even though offsettable_address_p considers (MEM
+ (LO_SUM)) to be offsettable, it is not safe if the
+ address is at the end of the data page since we also have
+ to fix up the associated high PART. In this case where
+ we are trying to split a HImode or HFmode memory
+ reference, we would have to emit another insn to reload a
+ new HIGH value. It's easier to disable LO_SUM memory references
+ in HImode or HFmode and we probably get better code. */
+ case LO_SUM:
+ fatal_insn ("c4x_operand_subword: address not offsettable", op);
+
+ default:
+ break;
+ }
+ }
+
+ return operand_subword (op, i, validate_address, mode);
+}
+
+/* Handle machine specific pragmas for compatibility with existing
+ compilers for the C3x/C4x.
+
+ pragma attribute
+ ----------------------------------------------------------
+ CODE_SECTION(symbol,"section") section("section")
+ DATA_SECTION(symbol,"section") section("section")
+ FUNC_CANNOT_INLINE(function)
+ FUNC_EXT_CALLED(function)
+ FUNC_IS_PURE(function) const
+ FUNC_IS_SYSTEM(function)
+ FUNC_NEVER_RETURNS(function) noreturn
+ FUNC_NO_GLOBAL_ASG(function)
+ FUNC_NO_IND_ASG(function)
+ INTERRUPT(function) interrupt
+
+ */
+
+int
+c4x_handle_pragma (p_getc, p_ungetc, pname)
+ int (* p_getc) PROTO ((void));
+ void (* p_ungetc) PROTO ((int)) ATTRIBUTE_UNUSED;
+ char *pname;
+{
+ int i;
+ int c;
+ int namesize;
+ char *name;
+ tree func;
+ tree sect = NULL_TREE;
+ tree new;
+
+ c = p_getc ();
+ while (c == ' ' || c == '\t') c = p_getc ();
+ if (c != '(')
+ return 0;
+
+ c = p_getc ();
+ while (c == ' ' || c == '\t') c = p_getc ();
+ if (! (isalpha(c) || c == '_' || c == '$' || c == '@'))
+ return 0;
+
+ i = 0;
+ namesize = 16;
+ name = xmalloc (namesize);
+ while (isalnum (c) || c == '_' || c == '$' || c == '@')
+ {
+ if (i >= namesize-1)
+ {
+ namesize += 16;
+ name = xrealloc (name, namesize);
+ }
+ name[i++] = c;
+ c = p_getc ();
+ }
+ name[i] = 0;
+ func = get_identifier (name);
+ free (name);
+
+ if (strcmp (pname, "CODE_SECTION") == 0
+ || strcmp (pname, "DATA_SECTION") == 0)
+ {
+ while (c == ' ' || c == '\t') c = p_getc ();
+ if (c != ',')
+ return 0;
+
+ c = p_getc ();
+ while (c == ' ' || c == '\t') c = p_getc ();
+ if (c != '"')
+ return 0;
+
+ i = 0;
+ namesize = 16;
+ name = xmalloc (namesize);
+ c = p_getc ();
+ while (c != '"' && c != '\n' && c != '\r' && c != EOF)
+ {
+ if (i >= namesize-1)
+ {
+ namesize += 16;
+ name = xrealloc (name, namesize);
+ }
+ name[i++] = c;
+ c = p_getc ();
+ }
+ name[i] = 0;
+ sect = build_string (i, name);
+ free (name);
+ sect = build_tree_list (NULL_TREE, sect);
+
+ if (c != '"')
+ return 0;
+ c = p_getc ();
+ }
+ while (c == ' ' || c == '\t') c = p_getc ();
+ if (c != ')')
+ return 0;
+
+ new = build_tree_list (func, sect);
+ if (strcmp (pname, "CODE_SECTION") == 0)
+ code_tree = chainon (code_tree, new);
+
+ else if (strcmp (pname, "DATA_SECTION") == 0)
+ data_tree = chainon (data_tree, new);
+
+ else if (strcmp (pname, "FUNC_CANNOT_INLINE") == 0)
+ ; /* ignore */
+
+ else if (strcmp (pname, "FUNC_EXT_CALLED") == 0)
+ ; /* ignore */
+
+ else if (strcmp (pname, "FUNC_IS_PURE") == 0)
+ pure_tree = chainon (pure_tree, new);
+
+ else if (strcmp (pname, "FUNC_IS_SYSTEM") == 0)
+ ; /* ignore */
+
+ else if (strcmp (pname, "FUNC_NEVER_RETURNS") == 0)
+ noreturn_tree = chainon (noreturn_tree, new);
+
+ else if (strcmp (pname, "FUNC_NO_GLOBAL_ASG") == 0)
+ ; /* ignore */
+
+ else if (strcmp (pname, "FUNC_NO_IND_ASG") == 0)
+ ; /* ignore */
+
+ else if (strcmp (pname, "INTERRUPT") == 0)
+ interrupt_tree = chainon (interrupt_tree, new);
+
+ else
+ return 0;
+
+ return 1;
+}
+
+
+static void
+c4x_check_attribute(attrib, list, decl, attributes)
+ char *attrib;
+ tree list, decl, *attributes;
+{
+ while (list != NULL_TREE
+ && IDENTIFIER_POINTER (TREE_PURPOSE (list))
+ != IDENTIFIER_POINTER (DECL_NAME (decl)))
+ list = TREE_CHAIN(list);
+ if (list)
+ *attributes = chainon (*attributes,
+ build_tree_list (get_identifier (attrib),
+ TREE_VALUE(list)));
+}
+
+
+void
+c4x_set_default_attributes(decl, attributes)
+ tree decl, *attributes;
+{
+ switch (TREE_CODE (decl))
+ {
+ case FUNCTION_DECL:
+ c4x_check_attribute ("section", code_tree, decl, attributes);
+ c4x_check_attribute ("const", pure_tree, decl, attributes);
+ c4x_check_attribute ("noreturn", noreturn_tree, decl, attributes);
+ c4x_check_attribute ("interrupt", interrupt_tree, decl, attributes);
+ break;
+
+ case VAR_DECL:
+ c4x_check_attribute ("section", data_tree, decl, attributes);
+ break;
+
+ default:
+ break;
+ }
+}
+
+
+/* Return nonzero if IDENTIFIER with arguments ARGS is a valid machine
+ specific attribute for TYPE. The attributes in ATTRIBUTES have
+ previously been assigned to TYPE. */
+
+int
+c4x_valid_type_attribute_p (type, attributes, identifier, args)
+ tree type;
+ tree attributes ATTRIBUTE_UNUSED;
+ tree identifier;
+ tree args ATTRIBUTE_UNUSED;
+{
+ if (TREE_CODE (type) != FUNCTION_TYPE)
+ return 0;
+
+ if (is_attribute_p ("interrupt", identifier))
+ return 1;
+
+ if (is_attribute_p ("assembler", identifier))
+ return 1;
+
+ if (is_attribute_p ("leaf_pretend", identifier))
+ return 1;
+
+ return 0;
+}
+
+
+/* !!! FIXME to emit RPTS correctly. */
+int
+c4x_rptb_rpts_p (insn, op)
+ rtx insn, op;
+{
+ /* The next insn should be our label marking where the
+ repeat block starts. */
+ insn = NEXT_INSN (insn);
+ if (GET_CODE (insn) != CODE_LABEL)
+ {
+ /* Some insns may have been shifted between the RPTB insn
+ and the top label... They were probably destined to
+ be moved out of the loop. For now, let's leave them
+ where they are and print a warning. We should
+ probably move these insns before the repeat block insn. */
+ if (TARGET_DEBUG)
+ fatal_insn("c4x_rptb_rpts_p: Repeat block top label moved\n",
+ insn);
+ return 0;
+ }
+
+ /* Skip any notes. */
+ insn = next_nonnote_insn (insn);
+
+ /* This should be our first insn in the loop. */
+ if (GET_RTX_CLASS (GET_CODE (insn)) != 'i')
+ return 0;
+
+ /* Skip any notes. */
+ insn = next_nonnote_insn (insn);
+
+ if (GET_RTX_CLASS (GET_CODE (insn)) != 'i')
+ return 0;
+
+ if (recog_memoized (insn) != CODE_FOR_rptb_end)
+ return 0;
+
+ if (TARGET_RPTS)
+ return 1;
+
+ return (GET_CODE (op) == CONST_INT) && TARGET_RPTS_CYCLES (INTVAL (op));
+}
+
+
+/* Adjust the cost of a scheduling dependency. Return the new cost of
+ a dependency LINK or INSN on DEP_INSN. COST is the current cost.
+ A set of an address register followed by a use occurs a 2 cycle
+ stall (reduced to a single cycle on the c40 using LDA), while
+ a read of an address register followed by a use occurs a single cycle. */
+#define SET_USE_COST 3
+#define SETLDA_USE_COST 2
+#define READ_USE_COST 2
+
+int
+c4x_adjust_cost (insn, link, dep_insn, cost)
+ rtx insn;
+ rtx link;
+ rtx dep_insn;
+ int cost;
+{
+ /* Don't worry about this until we know what registers have been
+ assigned. */
+ if (! reload_completed)
+ return 0;
+
+ /* How do we handle dependencies where a read followed by another
+ read causes a pipeline stall? For example, a read of ar0 followed
+ by the use of ar0 for a memory reference. It looks like we
+ need to extend the scheduler to handle this case. */
+
+ /* Reload sometimes generates a CLOBBER of a stack slot, e.g.,
+ (clobber (mem:QI (plus:QI (reg:QI 11 ar3) (const_int 261)))),
+ so only deal with insns we know about. */
+ if (recog_memoized (dep_insn) < 0)
+ return 0;
+
+ if (REG_NOTE_KIND (link) == 0)
+ {
+ int max = 0;
+
+ /* Data dependency; DEP_INSN writes a register that INSN reads some
+ cycles later. */
+ if (TARGET_C3X)
+ {
+ if (get_attr_setgroup1 (dep_insn) && get_attr_usegroup1 (insn))
+ max = SET_USE_COST > max ? SET_USE_COST : max;
+ if (get_attr_readarx (dep_insn) && get_attr_usegroup1 (insn))
+ max = READ_USE_COST > max ? READ_USE_COST : max;
+ }
+ else
+ {
+ /* This could be significantly optimized. We should look
+ to see if dep_insn sets ar0-ar7 or ir0-ir1 and if
+ insn uses ar0-ar7. We then test if the same register
+ is used. The tricky bit is that some operands will
+ use several registers... */
+ if (get_attr_setar0 (dep_insn) && get_attr_usear0 (insn))
+ max = SET_USE_COST > max ? SET_USE_COST : max;
+ if (get_attr_setlda_ar0 (dep_insn) && get_attr_usear0 (insn))
+ max = SETLDA_USE_COST > max ? SETLDA_USE_COST : max;
+ if (get_attr_readar0 (dep_insn) && get_attr_usear0 (insn))
+ max = READ_USE_COST > max ? READ_USE_COST : max;
+
+ if (get_attr_setar1 (dep_insn) && get_attr_usear1 (insn))
+ max = SET_USE_COST > max ? SET_USE_COST : max;
+ if (get_attr_setlda_ar1 (dep_insn) && get_attr_usear1 (insn))
+ max = SETLDA_USE_COST > max ? SETLDA_USE_COST : max;
+ if (get_attr_readar1 (dep_insn) && get_attr_usear1 (insn))
+ max = READ_USE_COST > max ? READ_USE_COST : max;
+
+ if (get_attr_setar2 (dep_insn) && get_attr_usear2 (insn))
+ max = SET_USE_COST > max ? SET_USE_COST : max;
+ if (get_attr_setlda_ar2 (dep_insn) && get_attr_usear2 (insn))
+ max = SETLDA_USE_COST > max ? SETLDA_USE_COST : max;
+ if (get_attr_readar2 (dep_insn) && get_attr_usear2 (insn))
+ max = READ_USE_COST > max ? READ_USE_COST : max;
+
+ if (get_attr_setar3 (dep_insn) && get_attr_usear3 (insn))
+ max = SET_USE_COST > max ? SET_USE_COST : max;
+ if (get_attr_setlda_ar3 (dep_insn) && get_attr_usear3 (insn))
+ max = SETLDA_USE_COST > max ? SETLDA_USE_COST : max;
+ if (get_attr_readar3 (dep_insn) && get_attr_usear3 (insn))
+ max = READ_USE_COST > max ? READ_USE_COST : max;
+
+ if (get_attr_setar4 (dep_insn) && get_attr_usear4 (insn))
+ max = SET_USE_COST > max ? SET_USE_COST : max;
+ if (get_attr_setlda_ar4 (dep_insn) && get_attr_usear4 (insn))
+ max = SETLDA_USE_COST > max ? SETLDA_USE_COST : max;
+ if (get_attr_readar4 (dep_insn) && get_attr_usear4 (insn))
+ max = READ_USE_COST > max ? READ_USE_COST : max;
+
+ if (get_attr_setar5 (dep_insn) && get_attr_usear5 (insn))
+ max = SET_USE_COST > max ? SET_USE_COST : max;
+ if (get_attr_setlda_ar5 (dep_insn) && get_attr_usear5 (insn))
+ max = SETLDA_USE_COST > max ? SETLDA_USE_COST : max;
+ if (get_attr_readar5 (dep_insn) && get_attr_usear5 (insn))
+ max = READ_USE_COST > max ? READ_USE_COST : max;
+
+ if (get_attr_setar6 (dep_insn) && get_attr_usear6 (insn))
+ max = SET_USE_COST > max ? SET_USE_COST : max;
+ if (get_attr_setlda_ar6 (dep_insn) && get_attr_usear6 (insn))
+ max = SETLDA_USE_COST > max ? SETLDA_USE_COST : max;
+ if (get_attr_readar6 (dep_insn) && get_attr_usear6 (insn))
+ max = READ_USE_COST > max ? READ_USE_COST : max;
+
+ if (get_attr_setar7 (dep_insn) && get_attr_usear7 (insn))
+ max = SET_USE_COST > max ? SET_USE_COST : max;
+ if (get_attr_setlda_ar7 (dep_insn) && get_attr_usear7 (insn))
+ max = SETLDA_USE_COST > max ? SETLDA_USE_COST : max;
+ if (get_attr_readar7 (dep_insn) && get_attr_usear7 (insn))
+ max = READ_USE_COST > max ? READ_USE_COST : max;
+
+ if (get_attr_setir0 (dep_insn) && get_attr_useir0 (insn))
+ max = SET_USE_COST > max ? SET_USE_COST : max;
+ if (get_attr_setlda_ir0 (dep_insn) && get_attr_useir0 (insn))
+ max = SETLDA_USE_COST > max ? SETLDA_USE_COST : max;
+
+ if (get_attr_setir1 (dep_insn) && get_attr_useir1 (insn))
+ max = SET_USE_COST > max ? SET_USE_COST : max;
+ if (get_attr_setlda_ir1 (dep_insn) && get_attr_useir1 (insn))
+ max = SETLDA_USE_COST > max ? SETLDA_USE_COST : max;
+ }
+
+ if (max)
+ cost = max;
+
+ /* For other data dependencies, the default cost specified in the
+ md is correct. */
+ return cost;
+ }
+ else if (REG_NOTE_KIND (link) == REG_DEP_ANTI)
+ {
+ /* Anti dependency; DEP_INSN reads a register that INSN writes some
+ cycles later. */
+
+ /* For c4x anti dependencies, the cost is 0. */
+ return 0;
+ }
+ else if (REG_NOTE_KIND (link) == REG_DEP_OUTPUT)
+ {
+ /* Output dependency; DEP_INSN writes a register that INSN writes some
+ cycles later. */
+
+ /* For c4x output dependencies, the cost is 0. */
+ return 0;
+ }
+ else
+ abort ();
+}
diff --git a/gnu/egcs/gcc/config/c4x/c4x.h b/gnu/egcs/gcc/config/c4x/c4x.h
new file mode 100644
index 00000000000..1dc8d5c63ed
--- /dev/null
+++ b/gnu/egcs/gcc/config/c4x/c4x.h
@@ -0,0 +1,2800 @@
+/* Definitions of target machine for GNU compiler. TMS320C[34]x
+ Copyright (C) 1994-98, 1999 Free Software Foundation, Inc.
+
+ Contributed by Michael Hayes (m.hayes@elec.canterbury.ac.nz)
+ and Herman Ten Brugge (Haj.Ten.Brugge@net.HCC.nl).
+
+ 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 1, 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. */
+
+/* Set the following so that some of the macros expand to function
+ calls to simplify debugging. */
+#define C4X_DEBUG 1
+
+/* RUN-TIME TARGET SPECIFICATION */
+
+#define C4x 1
+
+/* Name of the c4x assembler */
+
+#define ASM_PROG "c4x-as"
+
+/* Name of the c4x linker */
+
+#define LD_PROG "c4x-ld"
+
+/* Define assembler options */
+
+#define ASM_SPEC "\
+%{!mcpu=30:%{!mcpu=31:%{!mcpu=32:%{!mcpu=40:%{!mcpu=44:\
+%{!m30:%{!m40:-m40}}}}}}} \
+%{mcpu=30:-m30} \
+%{mcpu=31:-m31} \
+%{mcpu=32:-m32} \
+%{mcpu=40:-m40} \
+%{mcpu=44:-m44} \
+%{m30:-m30} \
+%{m31:-m31} \
+%{m32:-m32} \
+%{m40:-m40} \
+%{m44:-m44} \
+%{mmemparm:-p} %{mregparm:-r} \
+%{!mmemparm:%{!mregparm:-r}} \
+%{mbig:-b} %{msmall:-s} \
+%{!msmall:%{!mbig:-b}}"
+
+/* Define linker options */
+
+#define LINK_SPEC "\
+%{m30:--architecture c3x} \
+%{m31:--architecture c3x} \
+%{m32:--architecture c3x} \
+%{mcpu=30:--architecture c3x} \
+%{mcpu=31:--architecture c3x} \
+%{mcpu=32:--architecture c3x}"
+
+/* Define C preprocessor options. */
+
+#define CPP_SPEC "\
+%{!m30:%{!m31:%{!m32:%{!mcpu=30:%{!mcpu=31:%{!mcpu=32:%{!mcpu=40:%{!mcpu=44:\
+ %{!m40:%{!m44:-D_TMS320C4x -D_C4x -D_TMS320C40 -D_C40 }}}}}}}}}} \
+%{mcpu=30:-D_TMS320C3x -D_C3x -D_TMS320C30 -D_C30 } \
+%{m30:-D_TMS320C3x -D_C3x -D_TMS320C30 -D_C30 } \
+%{mcpu=31:-D_TMS320C3x -D_C3x -D_TMS320C31 -D_C31 } \
+%{m31:-D_TMS320C3x -D_C3x -D_TMS320C31 -D_C31 } \
+%{mcpu=32:-D_TMS320C3x -D_C3x -D_TMS320C32 -D_C32 } \
+%{m32:-D_TMS320C3x -D_C3x -D_TMS320C32 -D_C32 } \
+%{mcpu=40:-D_TMS320C4x -D_C4x -D_TMS320C40 -D_C40 } \
+%{m40:-D_TMS320C4x -D_C4x -D_TMS320C40 -D_C40 } \
+%{mcpu=44:-D_TMS320C4x -D_C4x -D_TMS320C44 -D_C44 } \
+%{m44:-D_TMS320C4x -D_C4x -D_TMS320C44 -D_C44 } \
+%{mmemparm:-U_REGPARM }%{mregparm:-D_REGPARM } \
+%{!mmemparm:%{!mregparm:-D_REGPARM }} \
+%{msmall:-U_BIGMODEL } %{mbig:-D_BIGMODEL } \
+%{!msmall:%{!mbig:-D_BIGMODEL }} \
+%{finline-functions:-D_INLINE }"
+
+/* Specify the startup file to link with. */
+
+#define STARTFILE_SPEC "\
+%{!mmemparm:%{m30:%{msmall:crt0_3sr%O%s} %{!msmall:crt0_3br%O%s}}} \
+%{mmemparm:%{m30:%{msmall:crt0_3sm%O%s} %{!msmall:crt0_3bm%O%s}}} \
+%{!mmemparm:%{m31:%{msmall:crt0_3sr%O%s} %{!msmall:crt0_3br%O%s}}} \
+%{mmemparm:%{m31:%{msmall:crt0_3sm%O%s} %{!msmall:crt0_3bm%O%s}}} \
+%{!mmemparm:%{m32:%{msmall:crt0_3sr%O%s} %{!msmall:crt0_3br%O%s}}} \
+%{mmemparm:%{m32:%{msmall:crt0_3sm%O%s} %{!msmall:crt0_3bm%O%s}}} \
+%{!mmemparm:%{mcpu=30:%{msmall:crt0_3sr%O%s} %{!msmall:crt0_3br%O%s}}} \
+%{mmemparm:%{mcpu=30:%{msmall:crt0_3sm%O%s} %{!msmall:crt0_3bm%O%s}}} \
+%{!mmemparm:%{mcpu=31:%{msmall:crt0_3sr%O%s} %{!msmall:crt0_3br%O%s}}} \
+%{mmemparm:%{mcpu=31:%{msmall:crt0_3sm%O%s} %{!msmall:crt0_3bm%O%s}}} \
+%{!mmemparm:%{mcpu=32:%{msmall:crt0_3sr%O%s} %{!msmall:crt0_3br%O%s}}} \
+%{mmemparm:%{mcpu=32:%{msmall:crt0_3sm%O%s} %{!msmall:crt0_3bm%O%s}}} \
+%{!mmemparm:%{m40:%{msmall:crt0_4sr%O%s} %{!msmall:crt0_4br%O%s}}} \
+%{mmemparm:%{m40:%{msmall:crt0_4sm%O%s} %{!msmall:crt0_4bm%O%s}}} \
+%{!mmemparm:%{m44:%{msmall:crt0_4sr%O%s} %{!msmall:crt0_4br%O%s}}} \
+%{mmemparm:%{m44:%{msmall:crt0_4sm%O%s} %{!msmall:crt0_4bm%O%s}}} \
+%{!mmemparm:%{mcpu=40:%{msmall:crt0_4sr%O%s} %{!msmall:crt0_4br%O%s}}} \
+%{mmemparm:%{mcpu=40:%{msmall:crt0_4sm%O%s} %{!msmall:crt0_4bm%O%s}}} \
+%{!mmemparm:%{mcpu=44:%{msmall:crt0_4sr%O%s} %{!msmall:crt0_4br%O%s}}} \
+%{mmemparm:%{mcpu=44:%{msmall:crt0_4sm%O%s} %{!msmall:crt0_4bm%O%s}}} \
+%{!mmemparm:%{!m30:%{!m31:%{!m32:%{!mcpu=30:%{!mcpu=31:%{!mcpu=32: \
+ %{!mcpu=40:%{!mcpu=44:%{!m40:%{!m44:%{msmall:crt0_4sr%O%s}}}}}}}}}}}} \
+%{mmemparm:%{!m30:%{!m31:%{!m32:%{!mcpu=30:%{!mcpu=31:%{!mcpu=32: \
+ %{!mcpu=40:%{!mcpu=44:%{!m40:%{!m44:%{msmall:crt0_4sm%O%s}}}}}}}}}}}} \
+%{!mmemparm:%{!m30:%{!m31:%{!m32:%{!mcpu=30:%{!mcpu=31:%{!mcpu=32: \
+ %{!mcpu=40:%{!mcpu=44:%{!m40:%{!m44:%{!msmall:crt0_4br%O%s}}}}}}}}}}}} \
+%{mmemparm:%{!m30:%{!m31:%{!m32:%{!mcpu=30:%{!mcpu=31:%{!mcpu=32: \
+ %{!mcpu=40:%{!mcpu=44:%{!m40:%{!m44:%{!msmall:crt0_4bm%O%s}}}}}}}}}}}}"
+
+/* Specify the end file to link with */
+
+#define ENDFILE_SPEC ""
+
+/* Target compilation option flags */
+
+#define SMALL_MEMORY_FLAG 0x0000001 /* small memory model */
+#define MPYI_FLAG 0x0000002 /* use 24-bit MPYI for C3x */
+#define FAST_FIX_FLAG 0x0000004 /* fast fixing of floats */
+#define RPTS_FLAG 0x0000008 /* allow use of RPTS */
+#define C3X_FLAG 0x0000010 /* emit C3x code */
+#define TI_FLAG 0x0000020 /* be compatible with TI assembler */
+#define PARANOID_FLAG 0x0000040 /* be paranoid about DP reg. in ISRs */
+#define MEMPARM_FLAG 0x0000080 /* pass arguments on stack */
+#define DEVEL_FLAG 0x0000100 /* enable features under development */
+#define RPTB_FLAG 0x0000200 /* enable repeat block */
+#define BK_FLAG 0x0000400 /* use BK as general register */
+#define DB_FLAG 0x0000800 /* use decrement and branch for C3x */
+#define DEBUG_FLAG 0x0001000 /* enable debugging of GCC */
+#define HOIST_FLAG 0x0002000 /* force constants into registers */
+#define LOOP_UNSIGNED_FLAG 0x0004000 /* allow unsigned loop counters */
+#define FORCE_FLAG 0x0008000 /* force op0 and op1 to be same */
+#define PRESERVE_FLOAT_FLAG 0x0010000 /* save all 40 bits for floats */
+#define PARALLEL_PACK_FLAG 0x0020000 /* allow parallel insn packing */
+#define PARALLEL_MPY_FLAG 0x0040000 /* allow MPY||ADD, MPY||SUB insns */
+#define ALIASES_FLAG 0x0080000 /* assume mem refs possibly aliased */
+
+#define C30_FLAG 0x0100000 /* emit C30 code */
+#define C31_FLAG 0x0200000 /* emit C31 code */
+#define C32_FLAG 0x0400000 /* emit C32 code */
+#define C40_FLAG 0x1000000 /* emit C40 code */
+#define C44_FLAG 0x2000000 /* emit C44 code */
+
+/* Run-time compilation parameters selecting different hardware subsets.
+
+ Macro to define tables used to set the flags.
+ This is a list in braces of triplets in braces,
+ each pair being { "NAME", VALUE, "DESCRIPTION" }
+ where VALUE is the bits to set or minus the bits to clear.
+ An empty string NAME is used to identify the default VALUE. */
+
+#define TARGET_SWITCHES \
+{ { "small", SMALL_MEMORY_FLAG, \
+ "Small memory model" }, \
+ { "big", -SMALL_MEMORY_FLAG, \
+ "Big memory model" }, \
+ { "mpyi", MPYI_FLAG, \
+ "Use MPYI instruction for C3x" }, \
+ { "no-mpyi", -MPYI_FLAG, \
+ "Do not use MPYI instruction for C3x" }, \
+ { "fast-fix", FAST_FIX_FLAG, \
+ "Use fast but approximate float to integer conversion" }, \
+ { "no-fast-fix", -FAST_FIX_FLAG, \
+ "Use slow but accurate float to integer conversion" }, \
+ { "rpts", RPTS_FLAG, \
+ "Enable use of RTPS instruction" }, \
+ { "no-rpts", -RPTS_FLAG, \
+ "Disable use of RTPS instruction" }, \
+ { "rptb", RPTB_FLAG, \
+ "Enable use of RTPB instruction" }, \
+ { "no-rptb", -RPTB_FLAG, \
+ "Disable use of RTPB instruction" }, \
+ { "30", C30_FLAG, \
+ "Generate code for C30 CPU"}, \
+ { "31", C31_FLAG, \
+ "Generate code for C31 CPU"}, \
+ { "32", C32_FLAG, \
+ "Generate code for C32 CPU"}, \
+ { "40", C40_FLAG, \
+ "Generate code for C40 CPU"}, \
+ { "44", C44_FLAG, \
+ "Generate code for C44 CPU"}, \
+ { "ti", TI_FLAG, \
+ "Emit code compatible with TI tools"}, \
+ { "no-ti", -TI_FLAG, \
+ "Emit code to use GAS extensions"}, \
+ { "paranoid", PARANOID_FLAG, \
+ "Save DP across ISR in small memory model" }, \
+ { "no-paranoid", -PARANOID_FLAG, \
+ "Don't save DP across ISR in small memory model" }, \
+ { "isr-dp-reload", PARANOID_FLAG, \
+ "Save DP across ISR in small memory model" }, \
+ { "no-isr-dp-reload", -PARANOID_FLAG, \
+ "Don't save DP across ISR in small memory model" }, \
+ { "memparm", MEMPARM_FLAG, \
+ "Pass arguments on the stack" }, \
+ { "regparm", -MEMPARM_FLAG, \
+ "Pass arguments in registers" }, \
+ { "devel", DEVEL_FLAG, \
+ "Enable new features under development" }, \
+ { "no-devel", -DEVEL_FLAG, \
+ "Disable new features under development" }, \
+ { "bk", BK_FLAG, \
+ "Use the BK register as a general purpose register" }, \
+ { "no-bk", -BK_FLAG, \
+ "Do not allocate BK register" }, \
+ { "db", DB_FLAG, \
+ "Enable use of DB instruction" }, \
+ { "no-db", -DB_FLAG, \
+ "Disable use of DB instruction" }, \
+ { "debug", DEBUG_FLAG, \
+ "Enable debugging" }, \
+ { "no-debug", -DEBUG_FLAG, \
+ "Disable debugging" }, \
+ { "hoist", HOIST_FLAG, \
+ "Force constants into registers to improve hoisting" }, \
+ { "no-hoist", -HOIST_FLAG, \
+ "Don't force constants into registers" }, \
+ { "force", FORCE_FLAG, \
+ "Force RTL generation to emit valid 3 operand insns" }, \
+ { "no-force", -FORCE_FLAG, \
+ "Allow RTL generation to emit invalid 3 operand insns" }, \
+ { "loop-unsigned", LOOP_UNSIGNED_FLAG, \
+ "Allow unsigned interation counts for RPTB/DB" }, \
+ { "no-loop-unsigned", -LOOP_UNSIGNED_FLAG, \
+ "Disallow unsigned iteration counts for RPTB/DB" }, \
+ { "preserve-float", PRESERVE_FLOAT_FLAG, \
+ "Preserve all 40 bits of FP reg across call" }, \
+ { "no-preserve-float", -PRESERVE_FLOAT_FLAG, \
+ "Only preserve 32 bits of FP reg across call" }, \
+ { "parallel-insns", PARALLEL_PACK_FLAG, \
+ "Enable parallel instructions" }, \
+ { "no-parallel-mpy", -PARALLEL_MPY_FLAG, \
+ "Disable parallel instructions" }, \
+ { "parallel-mpy", PARALLEL_MPY_FLAG, \
+ "Enable MPY||ADD and MPY||SUB instructions" }, \
+ { "no-parallel-insns", -PARALLEL_PACK_FLAG, \
+ "Disable MPY||ADD and MPY||SUB instructions" }, \
+ { "aliases", ALIASES_FLAG, \
+ "Assume that pointers may be aliased" }, \
+ { "no-aliases", -ALIASES_FLAG, \
+ "Assume that pointers not aliased" }, \
+ { "", TARGET_DEFAULT, ""} }
+
+/* Default target switches */
+
+/* Play safe, not the fastest code. */
+#define TARGET_DEFAULT ALIASES_FLAG | PARALLEL_PACK_FLAG \
+ | PARALLEL_MPY_FLAG | RPTB_FLAG
+
+/* Caveats:
+ Max iteration count for RPTB/RPTS is 2^31 + 1.
+ Max iteration count for DB is 2^31 + 1 for C40, but 2^23 + 1 for C30.
+ RPTS blocks interrupts. */
+
+
+extern int target_flags;
+
+#define TARGET_INLINE 1 /* Inline MPYI */
+#define TARGET_PARALLEL 1 /* Enable parallel insns in MD */
+#define TARGET_SMALL_REG_CLASS 0
+
+#define TARGET_SMALL (target_flags & SMALL_MEMORY_FLAG)
+#define TARGET_MPYI (!TARGET_C3X || (target_flags & MPYI_FLAG))
+#define TARGET_FAST_FIX (target_flags & FAST_FIX_FLAG)
+#define TARGET_RPTS (target_flags & RPTS_FLAG)
+#define TARGET_TI (target_flags & TI_FLAG)
+#define TARGET_PARANOID (target_flags & PARANOID_FLAG)
+#define TARGET_MEMPARM (target_flags & MEMPARM_FLAG)
+#define TARGET_DEVEL (target_flags & DEVEL_FLAG)
+#define TARGET_RPTB (target_flags & RPTB_FLAG \
+ && optimize >= 2)
+#define TARGET_BK (target_flags & BK_FLAG)
+#define TARGET_DB (! TARGET_C3X || (target_flags & DB_FLAG))
+#define TARGET_DEBUG (target_flags & DEBUG_FLAG)
+#define TARGET_HOIST (target_flags & HOIST_FLAG)
+#define TARGET_LOOP_UNSIGNED (target_flags & LOOP_UNSIGNED_FLAG)
+#define TARGET_FORCE (target_flags & FORCE_FLAG)
+#define TARGET_PRESERVE_FLOAT (target_flags & PRESERVE_FLOAT_FLAG)
+#define TARGET_PARALLEL_PACK (TARGET_RPTB \
+ && (target_flags & PARALLEL_PACK_FLAG) \
+ && optimize >= 2)
+#define TARGET_PARALLEL_MPY (TARGET_PARALLEL_PACK \
+ && (target_flags & PARALLEL_MPY_FLAG))
+#define TARGET_ALIASES (target_flags & ALIASES_FLAG)
+
+#define TARGET_C3X (target_flags & C3X_FLAG)
+#define TARGET_C30 (target_flags & C30_FLAG)
+#define TARGET_C31 (target_flags & C31_FLAG)
+#define TARGET_C32 (target_flags & C32_FLAG)
+#define TARGET_C40 (target_flags & C40_FLAG)
+#define TARGET_C44 (target_flags & C44_FLAG)
+
+#define TARGET_LOAD_ADDRESS (1 || (! TARGET_C3X && ! TARGET_SMALL))
+
+/* -mrpts allows the use of the RPTS instruction irregardless.
+ -mrpts=max-cycles will use RPTS if the number of cycles is constant
+ and less than max-cycles. */
+
+#define TARGET_RPTS_CYCLES(CYCLES) (TARGET_RPTS || (CYCLES) < c4x_rpts_cycles)
+
+#define BCT_CHECK_LOOP_ITERATIONS !(TARGET_LOOP_UNSIGNED)
+
+/* -mcpu=XX with XX = target DSP version number */
+
+/* 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.
+
+ Here is an example which defines `-mshort-data-NUMBER'. If the
+ given option is `-mshort-data-512', the variable `m88k_short_data'
+ will be set to the string `"512"'.
+
+ extern char *m88k_short_data;
+ #define TARGET_OPTIONS { { "short-data-", &m88k_short_data } } */
+
+extern char *c4x_rpts_cycles_string, *c4x_cpu_version_string;
+
+#define TARGET_OPTIONS \
+{ {"rpts=", &c4x_rpts_cycles_string, \
+ "Specify maximum number of iterations for RPTS" }, \
+ {"cpu=", &c4x_cpu_version_string, \
+ "Select CPU to generate code for" } }
+
+/* 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. */
+
+extern void c4x_override_options ();
+#define OVERRIDE_OPTIONS c4x_override_options ()
+
+/* Define this to change the optimizations performed by default. */
+extern void c4x_optimization_options ();
+#define OPTIMIZATION_OPTIONS(LEVEL,SIZE) c4x_optimization_options(LEVEL,SIZE)
+
+/* Run Time Target Specification */
+
+#define TARGET_VERSION fprintf (stderr, " (TMS320C[34]x, TI syntax)" );
+
+/* Storage Layout */
+
+#define BITS_BIG_ENDIAN 0
+#define BYTES_BIG_ENDIAN 0
+#define WORDS_BIG_ENDIAN 0
+
+/* Technically, we are little endian, but we put the floats out as
+ whole longs and this makes GCC put them out in the right order. */
+
+#define FLOAT_WORDS_BIG_ENDIAN 1
+
+/* Note the ANSI C standard requires sizeof(char) = 1. On the C[34]x
+ all integral and floating point data types are stored in memory as
+ 32-bits (floating point types can be stored as 40-bits in the
+ extended precision registers), so sizeof(char) = sizeof(short) =
+ sizeof(int) = sizeof(long) = sizeof(float) = sizeof(double) = 1. */
+
+#define BITS_PER_UNIT 32
+#define BITS_PER_WORD 32
+#define UNITS_PER_WORD 1
+#define POINTER_SIZE 32
+#define PARM_BOUNDARY 32
+#define STACK_BOUNDARY 32
+#define FUNCTION_BOUNDARY 32
+#define BIGGEST_ALIGNMENT 32
+#define EMPTY_FIELD_BOUNDARY 32
+#define STRICT_ALIGNMENT 0
+#define TARGET_FLOAT_FORMAT C4X_FLOAT_FORMAT
+#define MAX_FIXED_MODE_SIZE 64 /* HImode */
+
+/* Number of bits in the high and low parts of a two stage
+ load of an immediate constant. */
+#define BITS_PER_HIGH 16
+#define BITS_PER_LO_SUM 16
+
+/* Use the internal floating point stuff in the compiler and not the
+ host floating point stuff. */
+
+#define REAL_ARITHMETIC
+
+/* Define register numbers */
+
+/* Extended-precision registers */
+
+#define R0_REGNO 0
+#define R1_REGNO 1
+#define R2_REGNO 2
+#define R3_REGNO 3
+#define R4_REGNO 4
+#define R5_REGNO 5
+#define R6_REGNO 6
+#define R7_REGNO 7
+
+/* Auxiliary (address) registers */
+
+#define AR0_REGNO 8
+#define AR1_REGNO 9
+#define AR2_REGNO 10
+#define AR3_REGNO 11
+#define AR4_REGNO 12
+#define AR5_REGNO 13
+#define AR6_REGNO 14
+#define AR7_REGNO 15
+
+/* Data page register */
+
+#define DP_REGNO 16
+
+/* Index registers */
+
+#define IR0_REGNO 17
+#define IR1_REGNO 18
+
+/* Block size register */
+
+#define BK_REGNO 19
+
+/* Stack pointer */
+
+#define SP_REGNO 20
+
+/* Status register */
+
+#define ST_REGNO 21
+
+/* Misc. interrupt registers */
+
+#define DIE_REGNO 22 /* C4x only */
+#define IE_REGNO 22 /* C3x only */
+#define IIE_REGNO 23 /* C4x only */
+#define IF_REGNO 23 /* C3x only */
+#define IIF_REGNO 24 /* C4x only */
+#define IOF_REGNO 24 /* C3x only */
+
+/* Repeat block registers */
+
+#define RS_REGNO 25
+#define RE_REGNO 26
+#define RC_REGNO 27
+
+/* Additional extended-precision registers */
+
+#define R8_REGNO 28 /* C4x only */
+#define R9_REGNO 29 /* C4x only */
+#define R10_REGNO 30 /* C4x only */
+#define R11_REGNO 31 /* C4x only */
+
+#define FIRST_PSEUDO_REGISTER 32
+
+/* Extended precision registers (low set) */
+
+#define IS_R0R1_REG(r) ((((r) >= R0_REGNO) && ((r) <= R1_REGNO)))
+#define IS_R2R3_REG(r) ((((r) >= R2_REGNO) && ((r) <= R3_REGNO)))
+#define IS_EXT_LOW_REG(r) ((((r) >= R0_REGNO) && ((r) <= R7_REGNO)))
+
+/* Extended precision registers (high set) */
+
+#define IS_EXT_HIGH_REG(r) (! TARGET_C3X \
+ && ((r) >= R8_REGNO) && ((r) <= R11_REGNO))
+/* Address registers */
+
+#define IS_AUX_REG(r) (((r) >= AR0_REGNO) && ((r) <= AR7_REGNO))
+#define IS_ADDR_REG(r) IS_AUX_REG(r)
+#define IS_DP_REG(r) ((r) == DP_REGNO)
+#define IS_INDEX_REG(r) (((r) == IR0_REGNO) || ((r) == IR1_REGNO))
+#define IS_SP_REG(r) ((r) == SP_REGNO)
+#define IS_BK_REG(r) (TARGET_BK && (r) == BK_REGNO)
+
+/* Misc registers */
+
+#define IS_ST_REG(r) ((r) == ST_REGNO)
+#define IS_RC_REG(r) ((r) == RC_REGNO)
+#define IS_REPEAT_REG(r) (((r) >= RS_REGNO) && ((r) <= RC_REGNO))
+
+/* Composite register sets */
+
+#define IS_ADDR_OR_INDEX_REG(r) (IS_ADDR_REG(r) || IS_INDEX_REG(r))
+#define IS_EXT_REG(r) (IS_EXT_LOW_REG(r) || IS_EXT_HIGH_REG(r))
+#define IS_STD_REG(r) (IS_ADDR_OR_INDEX_REG(r) || IS_REPEAT_REG(r) \
+ || IS_SP_REG(r) || IS_BK_REG(r))
+#define IS_INT_REG(r) (IS_EXT_REG(r) || IS_STD_REG(r))
+#define IS_GROUP1_REG(r) (IS_ADDR_OR_INDEX_REG(r) || IS_BK_REG(r))
+
+
+#define IS_PSEUDO_REG(r) ((r) >= FIRST_PSEUDO_REGISTER)
+#define IS_R0R1_OR_PSEUDO_REG(r) (IS_R0R1_REG(r) || IS_PSEUDO_REG(r))
+#define IS_R2R3_OR_PSEUDO_REG(r) (IS_R2R3_REG(r) || IS_PSEUDO_REG(r))
+#define IS_EXT_OR_PSEUDO_REG(r) (IS_EXT_REG(r) || IS_PSEUDO_REG(r))
+#define IS_STD_OR_PSEUDO_REG(r) (IS_STD_REG(r) || IS_PSEUDO_REG(r))
+#define IS_INT_OR_PSEUDO_REG(r) (IS_INT_REG(r) || IS_PSEUDO_REG(r))
+#define IS_ADDR_OR_PSEUDO_REG(r) (IS_ADDR_REG(r) || IS_PSEUDO_REG(r))
+#define IS_INDEX_OR_PSEUDO_REG(r) (IS_INDEX_REG(r) || IS_PSEUDO_REG(r))
+#define IS_EXT_LOW_OR_PSEUDO_REG(r) (IS_EXT_LOW_REG(r) || IS_PSEUDO_REG(r))
+#define IS_DP_OR_PSEUDO_REG(r) (IS_DP_REG(r) || IS_PSEUDO_REG(r))
+#define IS_SP_OR_PSEUDO_REG(r) (IS_SP_REG(r) || IS_PSEUDO_REG(r))
+#define IS_ST_OR_PSEUDO_REG(r) (IS_ST_REG(r) || IS_PSEUDO_REG(r))
+#define IS_RC_OR_PSEUDO_REG(r) (IS_RC_REG(r) || IS_PSEUDO_REG(r))
+
+#define IS_PSEUDO_REGNO(op) (IS_PSEUDO_REG(REGNO(op)))
+#define IS_ADDR_REGNO(op) (IS_ADDR_REG(REGNO(op)))
+#define IS_INDEX_REGNO(op) (IS_INDEX_REG(REGNO(op)))
+#define IS_GROUP1_REGNO(r) (IS_GROUP1_REG(REGNO(op)))
+
+#define IS_R0R1_OR_PSEUDO_REGNO(op) (IS_R0R1_OR_PSEUDO_REG(REGNO(op)))
+#define IS_R2R3_OR_PSEUDO_REGNO(op) (IS_R2R3_OR_PSEUDO_REG(REGNO(op)))
+#define IS_EXT_OR_PSEUDO_REGNO(op) (IS_EXT_OR_PSEUDO_REG(REGNO(op)))
+#define IS_STD_OR_PSEUDO_REGNO(op) (IS_STD_OR_PSEUDO_REG(REGNO(op)))
+#define IS_EXT_LOW_OR_PSEUDO_REGNO(op) (IS_EXT_LOW_OR_PSEUDO_REG(REGNO(op)))
+#define IS_INT_OR_PSEUDO_REGNO(op) (IS_INT_OR_PSEUDO_REG(REGNO(op)))
+
+#define IS_ADDR_OR_PSEUDO_REGNO(op) (IS_ADDR_OR_PSEUDO_REG(REGNO(op)))
+#define IS_INDEX_OR_PSEUDO_REGNO(op) (IS_INDEX_OR_PSEUDO_REG(REGNO(op)))
+#define IS_DP_OR_PSEUDO_REGNO(op) (IS_DP_OR_PSEUDO_REG(REGNO(op)))
+#define IS_SP_OR_PSEUDO_REGNO(op) (IS_SP_OR_PSEUDO_REG(REGNO(op)))
+#define IS_ST_OR_PSEUDO_REGNO(op) (IS_ST_OR_PSEUDO_REG(REGNO(op)))
+#define IS_RC_OR_PSEUDO_REGNO(op) (IS_RC_OR_PSEUDO_REG(REGNO(op)))
+
+/* 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 AR0 AR1 AR2 AR3 AR4 AR5 AR6 AR7 */ \
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \
+/* DP IR0 IR1 BK SP ST DIE IIE IIF RS RE RC R8 R9 R10 R11 */ \
+ 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0 \
+}
+
+/* 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.
+
+ Note that the extended precision registers are only saved in some
+ modes. The macro HARD_REGNO_CALL_CLOBBERED specifies which modes
+ get clobbered for a given regno. */
+
+#define CALL_USED_REGISTERS \
+{ \
+/* R0 R1 R2 R3 R4 R5 R6 R7 AR0 AR1 AR2 AR3 AR4 AR5 AR6 AR7 */ \
+ 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, \
+/* DP IR0 IR1 BK SP ST DIE IIE IIF RS RE RC R8 R9 R10 R11 */ \
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1 \
+}
+
+/* Macro to conditionally modify fixed_regs/call_used_regs. */
+
+#define CONDITIONAL_REGISTER_USAGE \
+ { \
+ if (! TARGET_BK) \
+ { \
+ fixed_regs[BK_REGNO] = 1; \
+ call_used_regs[BK_REGNO] = 1; \
+ c4x_regclass_map[BK_REGNO] = NO_REGS; \
+ } \
+ if (TARGET_C3X) \
+ { \
+ int i; \
+ \
+ reg_names[DIE_REGNO] = "ie"; /* clobber die */ \
+ reg_names[IF_REGNO] = "if"; /* clobber iie */ \
+ reg_names[IOF_REGNO] = "iof"; /* clobber iif */ \
+ \
+ for (i = R8_REGNO; i <= R11_REGNO; i++) \
+ { \
+ fixed_regs[i] = call_used_regs[i] = 1; \
+ c4x_regclass_map[i] = NO_REGS; \
+ } \
+ } \
+ if (TARGET_PRESERVE_FLOAT) \
+ { \
+ c4x_caller_save_map[R6_REGNO] = HFmode; \
+ c4x_caller_save_map[R7_REGNO] = HFmode; \
+ } \
+ }
+
+/* Order of Allocation of Registers */
+
+/* List the order in which to allocate registers. Each register must be
+ listed once, even those in FIXED_REGISTERS.
+
+ First allocate registers that don't need preservation across calls,
+ except index and address registers. Then allocate data registers
+ that require preservation across calls (even though this invokes an
+ extra overhead of having to save/restore these registers). Next
+ allocate the address and index registers, since using these
+ registers for arithmetic can cause pipeline stalls. Finally
+ allocated the fixed registers which won't be allocated anyhow. */
+
+#define REG_ALLOC_ORDER \
+{R0_REGNO, R1_REGNO, R2_REGNO, R3_REGNO, \
+ R9_REGNO, R10_REGNO, R11_REGNO, \
+ RS_REGNO, RE_REGNO, RC_REGNO, BK_REGNO, \
+ R4_REGNO, R5_REGNO, R6_REGNO, R7_REGNO, R8_REGNO, \
+ AR0_REGNO, AR1_REGNO, AR2_REGNO, AR3_REGNO, \
+ AR4_REGNO, AR5_REGNO, AR6_REGNO, AR7_REGNO, \
+ IR0_REGNO, IR1_REGNO, \
+ SP_REGNO, DP_REGNO, ST_REGNO, IE_REGNO, IF_REGNO, IOF_REGNO}
+
+
+/* Determine which register classes are very likely used by spill registers.
+ local-alloc.c won't allocate pseudos that have these classes as their
+ preferred class unless they are "preferred or nothing". */
+
+#define CLASS_LIKELY_SPILLED_P(CLASS) \
+ ((CLASS) == INDEX_REGS)
+
+/* CCmode is wrongly defined in machmode.def It should have a size
+ of UNITS_PER_WORD. */
+
+#define HARD_REGNO_NREGS(REGNO, MODE) \
+(((MODE) == CCmode || (MODE) == CC_NOOVmode) ? 1 : ((MODE) == HFmode) ? 1 : \
+((GET_MODE_SIZE(MODE) + UNITS_PER_WORD - 1) / UNITS_PER_WORD))
+
+
+/* A C expression that is nonzero if the hard register REGNO is preserved
+ across a call in mode MODE. This does not have to include the call used
+ registers. */
+
+#define HARD_REGNO_CALL_PART_CLOBBERED(REGNO, MODE) \
+ ((((REGNO) == R6_REGNO || (REGNO) == R7_REGNO) && ! ((MODE) == QFmode)) \
+ || (((REGNO) == R4_REGNO || (REGNO) == R5_REGNO || (REGNO == R8_REGNO)) \
+ && ! ((MODE) == QImode || (MODE) == HImode || (MODE) == Pmode)))
+
+/* Specify the modes required to caller save a given hard regno. */
+
+#define HARD_REGNO_CALLER_SAVE_MODE(REGNO, NREGS) (c4x_caller_save_map[REGNO])
+
+int c4x_hard_regno_mode_ok ();
+#define HARD_REGNO_MODE_OK(REGNO, MODE) c4x_hard_regno_mode_ok(REGNO, MODE)
+
+
+/* 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.
+
+ Value is 1 if it is a good idea to tie two pseudo registers
+ when one has mode MODE1 and one has mode MODE2.
+ If HARD_REGNO_MODE_OK could produce different values for MODE1 and MODE2,
+ for any hard reg, then this must be 0 for correct output. */
+
+#define MODES_TIEABLE_P(MODE1, MODE2) 0
+
+
+/* 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,
+ R0R1_REGS, /* 't' */
+ R2R3_REGS, /* 'u' */
+ EXT_LOW_REGS, /* 'q' */
+ EXT_REGS, /* 'f' */
+ ADDR_REGS, /* 'a' */
+ INDEX_REGS, /* 'x' */
+ BK_REG, /* 'k' */
+ SP_REG, /* 'b' */
+ RC_REG, /* 'v' */
+ COUNTER_REGS, /* */
+ INT_REGS, /* 'c' */
+ GENERAL_REGS, /* 'r' */
+ DP_REG, /* 'z' */
+ ST_REG, /* 'y' */
+ ALL_REGS,
+ LIM_REG_CLASSES
+ };
+
+#define N_REG_CLASSES (int) LIM_REG_CLASSES
+
+#define REG_CLASS_NAMES \
+{ \
+ "NO_REGS", \
+ "R0R1_REGS", \
+ "R2R3_REGS", \
+ "EXT_LOW_REGS", \
+ "EXT_REGS", \
+ "ADDR_REGS", \
+ "INDEX_REGS", \
+ "BK_REG", \
+ "SP_REG", \
+ "RC_REG", \
+ "COUNTER_REGS", \
+ "INT_REGS", \
+ "GENERAL_REGS", \
+ "DP_REG", \
+ "ST_REG", \
+ "ALL_REGS" \
+}
+
+/* Define which registers fit in which classes.
+ This is an initializer for a vector of HARD_REG_SET
+ of length N_REG_CLASSES. RC is not included in GENERAL_REGS
+ since the register allocator will often choose a general register
+ in preference to RC for the decrement_and_branch_on_count pattern. */
+
+#define REG_CLASS_CONTENTS \
+{ \
+ {0x00000000}, /* No registers */ \
+ {0x00000003}, /* 't' R0-R1 */ \
+ {0x0000000c}, /* 'u' R2-R3 */ \
+ {0x000000ff}, /* 'q' R0-R7 */ \
+ {0xf00000ff}, /* 'f' R0-R11 */ \
+ {0x0000ff00}, /* 'a' AR0-AR7 */ \
+ {0x00060000}, /* 'x' IR0-IR1 */ \
+ {0x00080000}, /* 'k' BK */ \
+ {0x00100000}, /* 'b' SP */ \
+ {0x08000000}, /* 'v' RC */ \
+ {0x0800ff00}, /* RC,AR0-AR7 */ \
+ {0x0e1eff00}, /* 'c' AR0-AR7, IR0-IR1, BK, SP, RS, RE, RC */ \
+ {0xfe1effff}, /* 'r' R0-R11, AR0-AR7, IR0-IR1, BK, SP, RS, RE, RC */\
+ {0x00010000}, /* 'z' DP */ \
+ {0x00200000}, /* 'y' ST */ \
+ {0xffffffff}, /* All registers */ \
+}
+
+/* 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) (c4x_regclass_map[REGNO])
+
+/* When SMALL_REGISTER_CLASSES is defined, the lifetime of registers
+ explicitly used in the rtl is kept as short as possible.
+
+ We only need to define SMALL_REGISTER_CLASSES if TARGET_PARALLEL_MPY
+ is defined since the MPY|ADD insns require the classes R0R1_REGS and
+ R2R3_REGS which are used by the function return registers (R0,R1) and
+ the register arguments (R2,R3), respectively. I'm reluctant to define
+ this macro since it stomps on many potential optimisations. Ideally
+ it should have a register class argument so that not all the register
+ classes gets penalised for the sake of a naughty few... For long
+ double arithmetic we need two additional registers that we can use as
+ spill registers. */
+
+#define SMALL_REGISTER_CLASSES (TARGET_SMALL_REG_CLASS && TARGET_PARALLEL_MPY)
+
+#define BASE_REG_CLASS ADDR_REGS
+#define INDEX_REG_CLASS INDEX_REGS
+
+/*
+ Register constraints for the C4x
+
+ a - address reg (ar0-ar7)
+ b - stack reg (sp)
+ c - other gp int-only reg
+ d - data/int reg (equiv. to f)
+ f - data/float reg
+ h - data/long double reg (equiv. to f)
+ k - block count (bk)
+ q - r0-r7
+ t - r0-r1
+ u - r2-r3
+ v - repeat count (rc)
+ x - index register (ir0-ir1)
+ y - status register (st)
+ z - dp reg (dp)
+
+ Memory/constant constraints for the C4x
+
+ G - short float 16-bit
+ I - signed 16-bit constant (sign extended)
+ J - signed 8-bit constant (sign extended) (C4x only)
+ K - signed 5-bit constant (sign extended) (C4x only for stik)
+ L - unsigned 16-bit constant
+ M - unsigned 8-bit constant (C4x only)
+ N - ones complement of unsigned 16-bit constant
+ Q - indirect arx + 9-bit signed displacement
+ (a *-arx(n) or *+arx(n) is used to account for the sign bit)
+ R - indirect arx + 5-bit unsigned displacement (C4x only)
+ S - indirect arx + 0, 1, or irn displacement
+ T - direct symbol ref
+ > - indirect with autoincrement
+ < - indirect with autodecrement
+ } - indirect with post-modify
+ { - indirect with pre-modify
+ */
+
+#define REG_CLASS_FROM_LETTER(CC) \
+ ( ((CC) == 'a') ? ADDR_REGS \
+ : ((CC) == 'b') ? SP_REG \
+ : ((CC) == 'c') ? INT_REGS \
+ : ((CC) == 'd') ? EXT_REGS \
+ : ((CC) == 'f') ? EXT_REGS \
+ : ((CC) == 'h') ? EXT_REGS \
+ : ((CC) == 'k') ? BK_REG \
+ : ((CC) == 'q') ? EXT_LOW_REGS \
+ : ((CC) == 't') ? R0R1_REGS \
+ : ((CC) == 'u') ? R2R3_REGS \
+ : ((CC) == 'v') ? RC_REG \
+ : ((CC) == 'x') ? INDEX_REGS \
+ : ((CC) == 'y') ? ST_REG \
+ : ((CC) == 'z') ? DP_REG \
+ : NO_REGS )
+
+/* These assume that REGNO is a hard or pseudo reg number.
+ They give nonzero only if REGNO is a hard reg of the suitable class
+ or a pseudo reg currently allocated to a suitable hard reg.
+ Since they use reg_renumber, they are safe only once reg_renumber
+ has been allocated, which happens in local-alloc.c. */
+
+#define REGNO_OK_FOR_BASE_P(REGNO) \
+ (IS_ADDR_REG(REGNO) || IS_ADDR_REG((unsigned)reg_renumber[REGNO]))
+
+#define REGNO_OK_FOR_INDEX_P(REGNO) \
+ (IS_INDEX_REG(REGNO) || IS_INDEX_REG((unsigned)reg_renumber[REGNO]))
+
+extern enum reg_class c4x_preferred_reload_class ();
+#define PREFERRED_RELOAD_CLASS(X, CLASS) c4x_preferred_reload_class(X, CLASS)
+
+extern enum reg_class c4x_limit_reload_class ();
+#define LIMIT_RELOAD_CLASS(X, CLASS) c4x_limit_reload_class(X, CLASS)
+
+extern enum reg_class c4x_secondary_memory_needed ();
+#define SECONDARY_MEMORY_NEEDED(CLASS1, CLASS2, MODE) \
+c4x_secondary_memory_needed(CLASS1, CLASS2, MODE)
+
+#define CLASS_MAX_NREGS(CLASS, MODE) \
+(((MODE) == CCmode || (MODE) == CC_NOOVmode) ? 1 : ((MODE) == HFmode) ? 1 : \
+((GET_MODE_SIZE(MODE) + UNITS_PER_WORD - 1) / UNITS_PER_WORD))
+
+#define IS_INT5_CONST(VAL) (((VAL) <= 15) && ((VAL) >= -16)) /* 'K' */
+
+#define IS_UINT5_CONST(VAL) (((VAL) <= 31) && ((VAL) >= 0)) /* 'R' */
+
+#define IS_INT8_CONST(VAL) (((VAL) <= 127) && ((VAL) >= -128)) /* 'J' */
+
+#define IS_UINT8_CONST(VAL) (((VAL) <= 255) && ((VAL) >= 0)) /* 'M' */
+
+#define IS_INT16_CONST(VAL) (((VAL) <= 32767) && ((VAL) >= -32768)) /* 'I' */
+
+#define IS_UINT16_CONST(VAL) (((VAL) <= 65535) && ((VAL) >= 0)) /* 'L' */
+
+#define IS_NOT_UINT16_CONST(VAL) IS_UINT16_CONST(~(VAL)) /* 'N' */
+
+#define IS_HIGH_CONST(VAL) (! TARGET_C3X && (((VAL) & 0xffff) == 0)) /* 'O' */
+
+
+#define IS_DISP1_CONST(VAL) (((VAL) <= 1) && ((VAL) >= -1)) /* 'S' */
+
+#define IS_DISP8_CONST(VAL) (((VAL) <= 255) && ((VAL) >= -255)) /* 'Q' */
+
+#define IS_DISP1_OFF_CONST(VAL) (IS_DISP1_CONST (VAL) \
+ && IS_DISP1_CONST (VAL + 1))
+
+#define IS_DISP8_OFF_CONST(VAL) (IS_DISP8_CONST (VAL) \
+ && IS_DISP8_CONST (VAL + 1))
+
+#define CONST_OK_FOR_LETTER_P(VAL, C) \
+ ( ((C) == 'I') ? (IS_INT16_CONST (VAL)) \
+ : ((C) == 'J') ? (! TARGET_C3X && IS_INT8_CONST (VAL)) \
+ : ((C) == 'K') ? (! TARGET_C3X && IS_INT5_CONST (VAL)) \
+ : ((C) == 'L') ? (IS_UINT16_CONST (VAL)) \
+ : ((C) == 'M') ? (! TARGET_C3X && IS_UINT8_CONST (VAL)) \
+ : ((C) == 'N') ? (IS_NOT_UINT16_CONST (VAL)) \
+ : ((C) == 'O') ? (IS_HIGH_CONST (VAL)) \
+ : 0 )
+
+#define CONST_DOUBLE_OK_FOR_LETTER_P(OP, C) \
+ ( ((C) == 'G') ? (fp_zero_operand (OP)) \
+ : ((C) == 'H') ? (c4x_H_constant (OP)) \
+ : 0 )
+
+#define EXTRA_CONSTRAINT(OP, C) \
+ ( ((C) == 'Q') ? (c4x_Q_constraint (OP)) \
+ : ((C) == 'R') ? (c4x_R_constraint (OP)) \
+ : ((C) == 'S') ? (c4x_S_constraint (OP)) \
+ : ((C) == 'T') ? (c4x_T_constraint (OP)) \
+ : ((C) == 'U') ? (c4x_U_constraint (OP)) \
+ : 0 )
+
+#define SMALL_CONST(VAL, insn) \
+ ( ((insn == NULL_RTX) || (get_attr_data (insn) == DATA_INT16)) \
+ ? IS_INT16_CONST (VAL) \
+ : ( (get_attr_data (insn) == DATA_NOT_UINT16) \
+ ? IS_NOT_UINT16_CONST (VAL) \
+ : ( (get_attr_data (insn) == DATA_HIGH_16) \
+ ? IS_HIGH_CONST (VAL) \
+ : IS_UINT16_CONST (VAL) \
+ ) \
+ ) \
+ )
+
+/*
+ I. Routine calling with arguments in registers
+ ----------------------------------------------
+
+ The TI C3x compiler has a rather unusual register passing algorithm.
+ Data is passed in the following registers (in order):
+
+ AR2, R2, R3, RC, RS, RE
+
+ However, the first and second floating point values are always in R2
+ and R3 (and all other floats are on the stack). Structs are always
+ passed on the stack. If the last argument is an ellipsis, the
+ previous argument is passed on the stack so that its address can be
+ taken for the stdargs macros.
+
+ Because of this, we have to pre-scan the list of arguments to figure
+ out what goes where in the list.
+
+ II. Routine calling with arguments on stack
+ -------------------------------------------
+
+ Let the subroutine declared as "foo(arg0, arg1, arg2);" have local
+ variables loc0, loc1, and loc2. After the function prologue has
+ been executed, the stack frame will look like:
+
+ [stack grows towards increasing addresses]
+ I-------------I
+ 5 I saved reg1 I <= SP points here
+ I-------------I
+ 4 I saved reg0 I
+ I-------------I
+ 3 I loc2 I
+ I-------------I
+ 2 I loc1 I
+ I-------------I
+ 1 I loc0 I
+ I-------------I
+ 0 I old FP I <= FP (AR3) points here
+ I-------------I
+ -1 I return PC I
+ I-------------I
+ -2 I arg0 I
+ I-------------I
+ -3 I arg1 I
+ I-------------I
+ -4 I arg2 I
+ I-------------I
+
+ All local variables (locn) are accessible by means of +FP(n+1)
+ addressing, where n is the local variable number.
+
+ All stack arguments (argn) are accessible by means of -FP(n-2).
+
+ The stack pointer (SP) points to the last register saved in the
+ prologue (regn).
+
+ Note that a push instruction performs a preincrement of the stack
+ pointer. (STACK_PUSH_CODE == PRE_INC)
+
+ III. Registers used in function calling convention
+ --------------------------------------------------
+
+ Preserved across calls: R4...R5 (only by PUSH, i.e. lower 32 bits)
+ R6...R7 (only by PUSHF, i.e. upper 32 bits)
+ AR3...AR7
+
+ (Because of this model, we only assign FP values in R6, R7 and
+ only assign integer values in R4, R5.)
+
+ These registers are saved at each function entry and restored at
+ the exit. Also it is expected any of these not affected by any
+ call to user-defined (not service) functions.
+
+ Not preserved across calls: R0...R3
+ R4...R5 (upper 8 bits)
+ R6...R7 (lower 8 bits)
+ AR0...AR2, IR0, IR1, BK, ST, RS, RE, RC
+
+ These registers are used arbitrary in a function without being preserved.
+ It is also expected that any of these can be clobbered by any call.
+
+ Not used by GCC (except for in user "asm" statements):
+ IE (DIE), IF (IIE), IOF (IIF)
+
+ These registers are never used by GCC for any data, but can be used
+ with "asm" statements. */
+
+#define C4X_ARG0 -2
+#define C4X_LOC0 1
+
+/* Basic Stack Layout */
+
+/* The stack grows upward, stack frame grows upward, and args grow
+ downward. */
+
+#define STARTING_FRAME_OFFSET C4X_LOC0
+#define FIRST_PARM_OFFSET(FNDECL) (C4X_ARG0 + 1)
+#define ARGS_GROW_DOWNWARD
+#define STACK_POINTER_OFFSET 1
+
+/* Define this if pushing a word on the stack
+ makes the stack pointer a smaller address. */
+
+/* #define STACK_GROWS_DOWNWARD */
+/* Like the dsp16xx, i370, i960, and we32k ports */
+
+/* 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 */
+
+
+/* Registers That Address the Stack Frame */
+
+#define STACK_POINTER_REGNUM SP_REGNO /* SP */
+#define FRAME_POINTER_REGNUM AR3_REGNO /* AR3 */
+#define ARG_POINTER_REGNUM AR3_REGNO /* AR3 */
+#define STATIC_CHAIN_REGNUM AR0_REGNO /* AR0 */
+
+/* Eliminating Frame Pointer and Arg Pointer */
+
+#define FRAME_POINTER_REQUIRED 0
+
+#define INITIAL_FRAME_POINTER_OFFSET(DEPTH) \
+{ \
+ int regno; \
+ int offset = 0; \
+ for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++) \
+ if (regs_ever_live[regno] && ! call_used_regs[regno]) \
+ offset += TARGET_PRESERVE_FLOAT \
+ && ((regno == R6_REGNO) || (regno == R7_REGNO)) \
+ ? 2 : 1; \
+ (DEPTH) = -(offset + get_frame_size ()); \
+}
+
+/* This is a hack... We need to specify a register. */
+#define ELIMINABLE_REGS \
+ {{ FRAME_POINTER_REGNUM, FRAME_POINTER_REGNUM }}
+
+#define CAN_ELIMINATE(FROM, TO) \
+ (! (((FROM) == FRAME_POINTER_REGNUM && (TO) == STACK_POINTER_REGNUM) \
+ || ((FROM) == FRAME_POINTER_REGNUM && (TO) == FRAME_POINTER_REGNUM)))
+
+#define INITIAL_ELIMINATION_OFFSET(FROM, TO, OFFSET) \
+{ \
+ int regno; \
+ int offset = 0; \
+ for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++) \
+ if (regs_ever_live[regno] && ! call_used_regs[regno]) \
+ offset += TARGET_PRESERVE_FLOAT \
+ && ((regno == R6_REGNO) || (regno == R7_REGNO)) \
+ ? 2 : 1; \
+ (OFFSET) = -(offset + get_frame_size ()); \
+}
+
+
+/* Passing Function Arguments on the Stack */
+
+#if 0
+#define PUSH_ROUNDING(BYTES) (BYTES)
+#endif
+#define RETURN_POPS_ARGS(FUNDECL, FUNTYPE, STACK_SIZE) 0
+
+/* The following structure is used by calls.c, function.c, c4x.c */
+
+typedef struct c4x_args
+{
+ int floats;
+ int ints;
+ int maxfloats;
+ int maxints;
+ int init;
+ int var;
+ int prototype;
+ int args;
+}
+CUMULATIVE_ARGS;
+
+extern void c4x_init_cumulative_args();
+
+#define INIT_CUMULATIVE_ARGS(CUM,FNTYPE,LIBNAME,INDIRECT) \
+ (c4x_init_cumulative_args (&CUM, FNTYPE, LIBNAME))
+
+extern void c4x_function_arg_advance();
+
+#define FUNCTION_ARG_ADVANCE(CUM, MODE, TYPE, NAMED) \
+ (c4x_function_arg_advance (&CUM, MODE, TYPE, NAMED))
+
+extern struct rtx_def *c4x_function_arg();
+
+#define FUNCTION_ARG(CUM, MODE, TYPE, NAMED) \
+ (c4x_function_arg(&CUM, MODE, TYPE, NAMED))
+
+/* Define the profitability of saving registers around calls.
+ NOTE: For now we turn this off because caller-save assumes
+ that a register with a QFmode quantity can be saved/restored
+ using QImode. */
+
+/* #define CALLER_SAVE_PROFITABLE(REFS,CALLS) 0 */
+
+/* Never pass data by reference. */
+
+#define FUNCTION_ARG_PASS_BY_REFERENCE(CUM, MODE, TYPE, NAMED) 0
+
+#define FUNCTION_ARG_PARTIAL_NREGS(CUM, MODE, TYPE, NAMED) 0
+
+/* 1 if N is a possible register number for function argument passing. */
+
+#define FUNCTION_ARG_REGNO_P(REGNO) \
+ ( ( ((REGNO) == AR2_REGNO) /* AR2 */ \
+ || ((REGNO) == R2_REGNO) /* R2 */ \
+ || ((REGNO) == R3_REGNO) /* R3 */ \
+ || ((REGNO) == RC_REGNO) /* RC */ \
+ || ((REGNO) == RS_REGNO) /* RS */ \
+ || ((REGNO) == RE_REGNO)) /* RE */ \
+ ? 1 \
+ : 0)
+
+/* How Scalar Function Values Are Returned */
+
+#define FUNCTION_VALUE(VALTYPE, FUNC) \
+ gen_rtx(REG, TYPE_MODE(VALTYPE), R0_REGNO) /* Return in R0 */
+
+#define LIBCALL_VALUE(MODE) \
+ gen_rtx(REG, MODE, R0_REGNO) /* Return in R0 */
+
+#define FUNCTION_VALUE_REGNO_P(REGNO) ((REGNO) == R0_REGNO)
+
+/* How Large Values Are Returned */
+
+#define DEFAULT_PCC_STRUCT_RETURN 0
+#define STRUCT_VALUE_REGNUM AR0_REGNO /* AR0 */
+
+
+/* Function Entry and Exit */
+
+#define FUNCTION_PROLOGUE(FILE, SIZE) c4x_function_prologue(FILE, SIZE)
+#define FUNCTION_EPILOGUE(FILE, SIZE) c4x_function_epilogue(FILE, SIZE)
+
+
+/* Generating Code for Profiling */
+
+/* Note that the generated assembly uses the ^ operator to load the 16
+ MSBs of the address. This is not supported by the TI assembler. */
+
+#define FUNCTION_PROFILER(FILE, LABELNO) \
+ if (! TARGET_C3X) \
+ { \
+ fprintf (FILE, "\tpush\tar2\n"); \
+ fprintf (FILE, "\tldhi\t^LP%d,ar2\n", (LABELNO)); \
+ fprintf (FILE, "\tor\t#LP%d,ar2\n", (LABELNO)); \
+ fprintf (FILE, "\tcall\tmcount\n"); \
+ fprintf (FILE, "\tpop\tar2\n"); \
+ } \
+ else \
+ { \
+ fprintf (FILE, "\tpush\tar2\n"); \
+ fprintf (FILE, "\tldiu\t^LP%d,ar2\n", (LABELNO)); \
+ fprintf (FILE, "\tlsh\t16,ar2\n"); \
+ fprintf (FILE, "\tor\t#LP%d,ar2\n", (LABELNO)); \
+ fprintf (FILE, "\tcall\tmcount\n"); \
+ fprintf (FILE, "\tpop\tar2\n"); \
+ }
+
+/* There are three profiling modes for basic blocks available.
+ The modes are selected at compile time by using the options
+ -a or -ax of the gnu compiler.
+ The variable `profile_block_flag' will be set according to the
+ selected option.
+
+ profile_block_flag == 0, no option used:
+
+ No profiling done.
+
+ profile_block_flag == 1, -a option used.
+
+ Count frequency of execution of every basic block.
+
+ profile_block_flag == 2, -ax option used.
+
+ Generate code to allow several different profiling modes at run time.
+ Available modes are:
+ Produce a trace of all basic blocks.
+ Count frequency of jump instructions executed.
+ In every mode it is possible to start profiling upon entering
+ certain functions and to disable profiling of some other functions.
+
+ The result of basic-block profiling will be written to a file `bb.out'.
+ If the -ax option is used parameters for the profiling will be read
+ from file `bb.in'.
+
+*/
+
+#define FUNCTION_BLOCK_PROFILER(FILE, BLOCKNO) \
+ if (profile_block_flag == 2) \
+ { \
+ if (! TARGET_C3X) \
+ { \
+ fprintf (FILE, "\tpush\tst\n"); \
+ fprintf (FILE, "\tpush\tar2\n"); \
+ fprintf (FILE, "\tpush\tr2\n"); \
+ fprintf (FILE, "\tldhi\t^LPBX0,ar2\n"); \
+ fprintf (FILE, "\tor\t#LPBX0,ar2\n"); \
+ if (BLOCKNO > 32767) \
+ { \
+ fprintf (FILE, "\tldhi\t%d,r2\n", (BLOCKNO) >> 16); \
+ fprintf (FILE, "\tor\t%d,r2\n", (BLOCKNO)); \
+ } \
+ else \
+ { \
+ fprintf (FILE, "\tldiu\t%d,r2\n", (BLOCKNO)); \
+ } \
+ fprintf (FILE, "\tcall\t___bb_init_trace_func\n"); \
+ fprintf (FILE, "\tpop\tr2\n"); \
+ fprintf (FILE, "\tpop\tar2\n"); \
+ fprintf (FILE, "\tpop\tst\n"); \
+ } \
+ else \
+ { \
+ fprintf (FILE, "\tpush\tst\n"); \
+ fprintf (FILE, "\tpush\tar2\n"); \
+ fprintf (FILE, "\tpush\tr2\n"); \
+ fprintf (FILE, "\tldiu\t^LPBX0,ar2\n"); \
+ fprintf (FILE, "\tlsh\t16,ar2\n"); \
+ fprintf (FILE, "\tor\t#LPBX0,ar2\n"); \
+ if (BLOCKNO > 32767) \
+ { \
+ fprintf (FILE, "\tldi\t%d,r2\n", (BLOCKNO) >> 16); \
+ fprintf (FILE, "\tlsh\t16,r2\n"); \
+ fprintf (FILE, "\tor\t%d,r2\n", (BLOCKNO)); \
+ } \
+ else \
+ { \
+ fprintf (FILE, "\tldiu\t%d,r2\n", (BLOCKNO)); \
+ } \
+ fprintf (FILE, "\tcall\t___bb_init_trace_func\n"); \
+ fprintf (FILE, "\tpop\tr2\n"); \
+ fprintf (FILE, "\tpop\tar2\n"); \
+ fprintf (FILE, "\tpop\tst\n"); \
+ } \
+ } \
+ else \
+ { \
+ if (! TARGET_C3X) \
+ { \
+ fprintf (FILE, "\tpush\tst\n"); \
+ fprintf (FILE, "\tpush\tar2\n"); \
+ fprintf (FILE, "\tldhi\t^LPBX0,ar2\n"); \
+ fprintf (FILE, "\tor\t#LPBX0,ar2\n"); \
+ fprintf (FILE, "\tcmpi\t0,*ar2\n"); \
+ fprintf (FILE, "\tbne\t$+2\n"); \
+ fprintf (FILE, "\tcall\t___bb_init_func\n"); \
+ fprintf (FILE, "\tpop\tar2\n"); \
+ fprintf (FILE, "\tpop\tst\n"); \
+ } \
+ else \
+ { \
+ fprintf (FILE, "\tpush\tst\n"); \
+ fprintf (FILE, "\tpush\tar2\n"); \
+ fprintf (FILE, "\tpush\tr2\n"); \
+ fprintf (FILE, "\tldiu\t^LPBX0,ar2\n"); \
+ fprintf (FILE, "\tlsh\t16,ar2\n"); \
+ fprintf (FILE, "\tor\t#LPBX0,ar2\n"); \
+ fprintf (FILE, "\tldi\t*ar2,r2\n"); \
+ fprintf (FILE, "\tbne\t$+2\n"); \
+ fprintf (FILE, "\tcall\t___bb_init_func\n"); \
+ fprintf (FILE, "\tpop\tr2\n"); \
+ fprintf (FILE, "\tpop\tar2\n"); \
+ fprintf (FILE, "\tpop\tst\n"); \
+ } \
+ }
+
+#define BLOCK_PROFILER(FILE, BLOCKNO) \
+ if (profile_block_flag == 2) \
+ { \
+ if (! TARGET_C3X) \
+ { \
+ fprintf (FILE, "\tpush\tst\n"); \
+ fprintf (FILE, "\tpush\tar2\n"); \
+ fprintf (FILE, "\tpush\tar0\n"); \
+ fprintf (FILE, "\tldhi\t^___bb,ar2\n"); \
+ fprintf (FILE, "\tor\t#___bb,ar2\n"); \
+ if (BLOCKNO > 32767) \
+ { \
+ fprintf (FILE, "\tldhi\t%d,ar0\n", (BLOCKNO) >> 16);\
+ fprintf (FILE, "\tor\t%d,ar0\n", (BLOCKNO)); \
+ } \
+ else \
+ { \
+ fprintf (FILE, "\tldiu\t%d,ar0\n", (BLOCKNO)); \
+ } \
+ fprintf (FILE, "\tsti\tar0,*ar2\n"); \
+ fprintf (FILE, "\tldhi\t^LPBX0,ar0\n"); \
+ fprintf (FILE, "\tor\t#LPBX0,ar0\n"); \
+ fprintf (FILE, "\tsti\tar0,*+ar2(1)\n"); \
+ fprintf (FILE, "\tcall\t___bb_trace_func\n"); \
+ fprintf (FILE, "\tpop\tar0\n"); \
+ fprintf (FILE, "\tpop\tar2\n"); \
+ fprintf (FILE, "\tpop\tst\n"); \
+ } \
+ else \
+ { \
+ fprintf (FILE, "\tpush\tst\n"); \
+ fprintf (FILE, "\tpush\tar2\n"); \
+ fprintf (FILE, "\tpush\tar0\n"); \
+ fprintf (FILE, "\tldiu\t^___bb,ar2\n"); \
+ fprintf (FILE, "\tlsh\t16,ar2\n"); \
+ fprintf (FILE, "\tor\t#___bb,ar2\n"); \
+ if (BLOCKNO > 32767) \
+ { \
+ fprintf (FILE, "\tldi\t%d,ar0\n", (BLOCKNO) >> 16); \
+ fprintf (FILE, "\tlsh\t16,ar0\n"); \
+ fprintf (FILE, "\tor\t%d,ar0\n", (BLOCKNO)); \
+ } \
+ else \
+ { \
+ fprintf (FILE, "\tldiu\t%d,ar0\n", (BLOCKNO)); \
+ } \
+ fprintf (FILE, "\tsti\tar0,*ar2\n"); \
+ fprintf (FILE, "\tldiu\t^LPBX0,ar0\n"); \
+ fprintf (FILE, "\tlsh\t16,ar0\n"); \
+ fprintf (FILE, "\tor\t#LPBX0,ar0\n"); \
+ fprintf (FILE, "\tsti\tar0,*+ar2(1)\n"); \
+ fprintf (FILE, "\tcall\t___bb_trace_func\n"); \
+ fprintf (FILE, "\tpop\tar0\n"); \
+ fprintf (FILE, "\tpop\tar2\n"); \
+ fprintf (FILE, "\tpop\tst\n"); \
+ } \
+ } \
+ else \
+ { \
+ if (! TARGET_C3X) \
+ { \
+ fprintf (FILE, "\tpush\tar2\n"); \
+ fprintf (FILE, "\tpush\tar0\n"); \
+ fprintf (FILE, "\tldhi\t^LPBX2+%d,ar2\n", (BLOCKNO)); \
+ fprintf (FILE, "\tor\t#LPBX2+%d,ar2\n", (BLOCKNO)); \
+ fprintf (FILE, "\taddi3\t1,*ar2,ar0\n"); \
+ fprintf (FILE, "\tsti\tar0,*ar2\n"); \
+ fprintf (FILE, "\tpop\tar0\n"); \
+ fprintf (FILE, "\tpop\tar2\n"); \
+ } \
+ else \
+ { \
+ fprintf (FILE, "\tpush\tar2\n"); \
+ fprintf (FILE, "\tpush\tar0\n"); \
+ fprintf (FILE, "\tldiu\t^LPBX2+%d,ar2\n", (BLOCKNO)); \
+ fprintf (FILE, "\tlsh\t16,ar2\n"); \
+ fprintf (FILE, "\tor\t#LPBX2+%d,ar2\n", (BLOCKNO)); \
+ fprintf (FILE, "\tldiu\t*ar2,ar0\n"); \
+ fprintf (FILE, "\taddi\t1,ar0\n"); \
+ fprintf (FILE, "\tsti\tar0,*ar2\n"); \
+ fprintf (FILE, "\tpop\tar0\n"); \
+ fprintf (FILE, "\tpop\tar2\n"); \
+ } \
+ }
+
+#define FUNCTION_BLOCK_PROFILER_EXIT(FILE) \
+ { \
+ fprintf (FILE, "\tpush\tst\n"); \
+ fprintf (FILE, "\tpush\tar2\n"); \
+ fprintf (FILE, "\tcall\t___bb_trace_ret\n"); \
+ fprintf (FILE, "\tpop\tar2\n"); \
+ fprintf (FILE, "\tpop\tst\n"); \
+ }
+
+#define MACHINE_STATE_SAVE(ID) \
+ asm(" push r0"); \
+ asm(" pushf r0"); \
+ asm(" push r1"); \
+ asm(" pushf r1"); \
+ asm(" push r2"); \
+ asm(" pushf r2"); \
+ asm(" push r3"); \
+ asm(" pushf r3"); \
+ asm(" push ar0"); \
+ asm(" push ar1"); \
+ asm(" .if .BIGMODEL"); \
+ asm(" push dp"); \
+ asm(" .endif"); \
+ asm(" push ir0"); \
+ asm(" push ir1"); \
+ asm(" push bk"); \
+ asm(" push rs"); \
+ asm(" push re"); \
+ asm(" push rc"); \
+ asm(" .if .tms320C40"); \
+ asm(" push r9"); \
+ asm(" pushf r9"); \
+ asm(" push r10"); \
+ asm(" pushf r10"); \
+ asm(" push r11"); \
+ asm(" pushf r11"); \
+ asm(" .endif");
+
+#define MACHINE_STATE_RESTORE(ID) \
+ asm(" .if .tms320C40"); \
+ asm(" popf r11"); \
+ asm(" pop r11"); \
+ asm(" popf r10"); \
+ asm(" pop r10"); \
+ asm(" popf r9"); \
+ asm(" pop r9"); \
+ asm(" .endif"); \
+ asm(" pop rc"); \
+ asm(" pop re"); \
+ asm(" pop rs"); \
+ asm(" pop bk"); \
+ asm(" pop ir1"); \
+ asm(" pop ir0"); \
+ asm(" .if .BIGMODEL"); \
+ asm(" pop dp"); \
+ asm(" .endif"); \
+ asm(" pop ar1"); \
+ asm(" pop ar0"); \
+ asm(" popf r3"); \
+ asm(" pop r3"); \
+ asm(" popf r2"); \
+ asm(" pop r2"); \
+ asm(" popf r1"); \
+ asm(" pop r1"); \
+ asm(" popf r0"); \
+ asm(" pop r0"); \
+
+/* Implicit Calls to Library Routines */
+
+#define MULQI3_LIBCALL "__mulqi3"
+#define DIVQI3_LIBCALL "__divqi3"
+#define UDIVQI3_LIBCALL "__udivqi3"
+#define MODQI3_LIBCALL "__modqi3"
+#define UMODQI3_LIBCALL "__umodqi3"
+
+#define DIVQF3_LIBCALL "__divqf3"
+
+#define MULHF3_LIBCALL "__mulhf3"
+#define DIVHF3_LIBCALL "__divhf3"
+
+#define MULHI3_LIBCALL "__mulhi3"
+#define SMULHI3_LIBCALL "__smulhi3_high"
+#define UMULHI3_LIBCALL "__umulhi3_high"
+#define DIVHI3_LIBCALL "__divhi3"
+#define UDIVHI3_LIBCALL "__udivhi3"
+#define MODHI3_LIBCALL "__modhi3"
+#define UMODHI3_LIBCALL "__umodhi3"
+
+#define FLOATHIQF2_LIBCALL "__floathiqf2"
+#define FLOATUNSHIQF2_LIBCALL "__ufloathiqf2"
+#define FIX_TRUNCQFHI2_LIBCALL "__fix_truncqfhi2"
+#define FIXUNS_TRUNCQFHI2_LIBCALL "__ufix_truncqfhi2"
+
+#define FLOATHIHF2_LIBCALL "__floathihf2"
+#define FLOATUNSHIHF2_LIBCALL "__ufloathihf2"
+#define FIX_TRUNCHFHI2_LIBCALL "__fix_trunchfhi2"
+#define FIXUNS_TRUNCHFHI2_LIBCALL "__ufix_trunchfhi2"
+
+#define FFS_LIBCALL "__ffs"
+
+#define TARGET_MEM_FUNCTIONS
+
+/* Add any extra modes needed to represent the condition code.
+
+ On the C4x, we have a "no-overflow" mode which is used when an ADD,
+ SUB, NEG, or MPY insn is used to set the condition code. This is
+ to prevent the combiner from optimising away a following CMP of the
+ result with zero when a signed conditional branch or load insn
+ follows.
+
+ The problem is a subtle one and deals with the manner in which the
+ negative condition (N) flag is used on the C4x. This flag does not
+ reflect the status of the actual result but of the ideal result had
+ no overflow occured (when considering signed operands).
+
+ For example, 0x7fffffff + 1 => 0x80000000 Z=0 V=1 N=0 C=0. Here
+ the flags reflect the untruncated result, not the actual result.
+ While the actual result is less than zero, the N flag is not set
+ since the ideal result of the addition without truncation would
+ have been positive.
+
+ Note that the while the N flag is handled differently to most other
+ architectures, the use of it is self consistent and is not the
+ cause of the problem.
+
+ Logical operations set the N flag to the MSB of the result so if
+ the result is negative, N is 1. However, integer and floating
+ point operations set the N flag to be the MSB of the result
+ exclusive ored with the overflow (V) flag. Thus if an overflow
+ occurs and the result does not have the MSB set (i.e., the result
+ looks like a positive number), the N flag is set. Conversely, if
+ an overflow occurs and the MSB of the result is set, N is set to 0.
+ Thus the N flag represents the sign of the result if it could have
+ been stored without overflow but does not represent the apparent
+ sign of the result. Note that most architectures set the N flag to
+ be the MSB of the result.
+
+ The C4x approach to setting the N flag simplifies signed
+ conditional branches and loads which only have to test the state of
+ the N flag, whereas most architectures have to look at both the N
+ and V flags. The disadvantage is that there is no flag giving the
+ status of the sign bit of the operation. However, there are no
+ conditional load or branch instructions that make use of this
+ feature (e.g., BMI---branch minus) instruction. Note that BN and
+ BLT are identical in the C4x.
+
+ To handle the problem where the N flag is set differently whenever
+ there is an overflow we use a different CC mode, CC_NOOVmode which
+ says that the CC reflects the comparison of the result against zero
+ if no overflow occured.
+
+ For example,
+
+ [(set (reg:CC_NOOV 21)
+ (compare:CC_NOOV (minus:QI (match_operand:QI 1 "src_operand" "")
+ (match_operand:QI 2 "src_operand" ""))
+ (const_int 0)))
+ (set (match_operand:QI 0 "ext_reg_operand" "")
+ (minus:QI (match_dup 1)
+ (match_dup 2)))]
+
+ Note that there is no problem for insns that don't return a result
+ like CMP, since the CC reflects the effect of operation.
+
+ An example of a potential problem is when GCC
+ converts (LTU (MINUS (0x80000000) (0x7fffffff) (0x80000000)))
+ to (LEU (MINUS (0x80000000) (0x7fffffff) (0x7fffffff)))
+ to (GE (MINUS (0x80000000) (0x7fffffff) (0x00000000)))
+
+ Now (MINUS (0x80000000) (0x7fffffff)) returns 0x00000001 but the
+ C4x sets the N flag since the result without overflow would have
+ been 0xffffffff when treating the operands as signed integers.
+ Thus (GE (MINUS (0x80000000) (0x7fffffff) (0x00000000))) sets the N
+ flag but (GE (0x00000001)) does not set the N flag.
+
+ The upshot is that we can not use signed branch and conditional
+ load instructions after an add, subtract, neg, abs or multiply.
+ We must emit a compare insn to check the result against 0. */
+
+#define EXTRA_CC_MODES CC_NOOVmode
+
+/* Define the names for the modes specified above. */
+
+#define EXTRA_CC_NAMES "CC_NOOV"
+
+/* CC_NOOVmode should be used when the first operand is a PLUS, MINUS, NEG
+ or MULT.
+ CCmode should be used when no special processing is needed. */
+#define SELECT_CC_MODE(OP,X,Y) \
+ ((GET_CODE (X) == PLUS || GET_CODE (X) == MINUS \
+ || GET_CODE (X) == NEG || GET_CODE (X) == MULT \
+ || GET_MODE (X) == ABS \
+ || GET_CODE (Y) == PLUS || GET_CODE (Y) == MINUS \
+ || GET_CODE (Y) == NEG || GET_CODE (Y) == MULT \
+ || GET_MODE (Y) == ABS) \
+ ? CC_NOOVmode : CCmode)
+
+extern struct rtx_def *c4x_gen_compare_reg ();
+
+/* Addressing Modes */
+
+#define HAVE_POST_INCREMENT 1
+#define HAVE_PRE_INCREMENT 1
+#define HAVE_POST_DECREMENT 1
+#define HAVE_PRE_DECREMENT 1
+#define HAVE_PRE_MODIFY_REG 1
+#define HAVE_POST_MODIFY_REG 1
+#define HAVE_PRE_MODIFY_DISP 1
+#define HAVE_POST_MODIFY_DISP 1
+
+/* The number of insns that can be packed into a single opcode. */
+#define MULTIPACK_INSNS 2
+
+/* Recognize any constant value that is a valid address.
+ We could allow arbitrary constant addresses in the large memory
+ model but for the small memory model we can only accept addresses
+ within the data page. I suppose we could also allow
+ CONST PLUS SYMBOL_REF. */
+#define CONSTANT_ADDRESS_P(X) (GET_CODE (X) == SYMBOL_REF)
+
+/* Maximum number of registers that can appear in a valid memory
+ address. */
+#define MAX_REGS_PER_ADDRESS 2
+
+/* The macros REG_OK_FOR..._P assume that the arg is a REG rtx
+ and check its validity for a certain class.
+ We have two alternate definitions for each of them.
+ The usual definition accepts all pseudo regs; the other rejects
+ them unless they have been allocated suitable hard regs.
+ The symbol REG_OK_STRICT causes the latter definition to be used.
+
+ Most source files want to accept pseudo regs in the hope that
+ they will get allocated to the class that the insn wants them to be in.
+ Source files for reload pass need to be strict.
+ After reload, it makes no difference, since pseudo regs have
+ been eliminated by then. */
+
+extern int c4x_check_legit_addr ();
+
+#ifndef REG_OK_STRICT
+
+/* Nonzero if X is a hard or pseudo reg that can be used as an base. */
+
+#define REG_OK_FOR_BASE_P(X) IS_ADDR_OR_PSEUDO_REG(REGNO(X))
+
+/* Nonzero if X is a hard or pseudo reg that can be used as an index. */
+
+#define REG_OK_FOR_INDEX_P(X) IS_INDEX_OR_PSEUDO_REG(REGNO(X))
+
+#define GO_IF_LEGITIMATE_ADDRESS(MODE, X, ADDR) \
+{ \
+ if (c4x_check_legit_addr (MODE, X, 0)) \
+ goto ADDR; \
+}
+
+#else
+
+/* Nonzero if X is a hard reg that can be used as an index. */
+
+#define REG_OK_FOR_INDEX_P(X) REGNO_OK_FOR_INDEX_P (REGNO (X))
+
+/* Nonzero if X is a hard reg that can be used as a base reg. */
+
+#define REG_OK_FOR_BASE_P(X) REGNO_OK_FOR_BASE_P (REGNO (X))
+
+#define GO_IF_LEGITIMATE_ADDRESS(MODE, X, ADDR) \
+{ \
+ if (c4x_check_legit_addr (MODE, X, 1)) \
+ goto ADDR; \
+}
+
+#endif
+
+extern struct rtx_def *c4x_legitimize_address ();
+#define LEGITIMIZE_ADDRESS(X, OLDX, MODE, WIN) \
+{ \
+ rtx new; \
+ new = c4x_legitimize_address (X, MODE); \
+ if (new != NULL_RTX) \
+ { \
+ (X) = new; \
+ goto WIN; \
+ } \
+}
+
+
+/* No mode-dependent addresses on the C4x are autoincrements. */
+
+#define GO_IF_MODE_DEPENDENT_ADDRESS(ADDR, LABEL) \
+ if (GET_CODE (ADDR) == PRE_DEC \
+ || GET_CODE (ADDR) == POST_DEC \
+ || GET_CODE (ADDR) == PRE_INC \
+ || GET_CODE (ADDR) == POST_INC \
+ || GET_CODE (ADDR) == POST_MODIFY \
+ || GET_CODE (ADDR) == PRE_MODIFY) \
+ goto LABEL
+
+
+/* Nonzero if the constant value X is a legitimate general operand.
+ It is given that X satisfies CONSTANT_P or is a CONST_DOUBLE.
+
+ The C4x can only load 16-bit immediate values, so we only allow a
+ restricted subset of CONST_INT and CONST_DOUBLE. Disallow
+ LABEL_REF and SYMBOL_REF (except on the C40 with the big memory
+ model) so that the symbols will be forced into the constant pool.
+ On second thoughts, lets do this with the move expanders.
+*/
+
+#define LEGITIMATE_CONSTANT_P(X) \
+ ((GET_CODE (X) == CONST_DOUBLE && c4x_H_constant (X)) \
+ || (GET_CODE (X) == CONST_INT && c4x_I_constant (X)) \
+ || (GET_CODE (X) == SYMBOL_REF) \
+ || (GET_CODE (X) == LABEL_REF) \
+ || (GET_CODE (X) == CONST) \
+ || (GET_CODE (X) == HIGH && ! TARGET_C3X) \
+ || (GET_CODE (X) == LO_SUM && ! TARGET_C3X))
+
+#define LEGITIMATE_DISPLACEMENT_P(X) IS_DISP8_CONST (INTVAL (X))
+
+/* Define this macro if references to a symbol must be treated
+ differently depending on something about the variable or
+ function named by the symbol (such as what section it is in).
+
+ The macro definition, if any, is executed immediately after the
+ rtl for DECL or other node is created.
+ The value of the rtl will be a `mem' whose address is a
+ `symbol_ref'.
+
+ The usual thing for this macro to do is to a flag in the
+ `symbol_ref' (such as `SYMBOL_REF_FLAG') or to store a modified
+ name string in the `symbol_ref' (if one bit is not enough
+ information).
+
+ On the C4x we use this to indicate if a symbol is in text or
+ data space. */
+
+extern void c4x_encode_section_info ();
+#define ENCODE_SECTION_INFO(DECL) c4x_encode_section_info (DECL);
+
+/* Descripting Relative Cost of Operations */
+
+/* Provide the costs of a rtl expression. This is in the body of a
+ switch on CODE.
+
+ Note that we return, rather than break so that rtx_cost doesn't
+ include CONST_COSTS otherwise expand_mult will think that it is
+ cheaper to synthesise a multiply rather than to use a multiply
+ instruction. I think this is because the algorithm synth_mult
+ doesn't take into account the loading of the operands, whereas the
+ calculation of mult_cost does.
+*/
+
+
+#define RTX_COSTS(RTX, CODE, OUTER_CODE) \
+ case PLUS: \
+ case MINUS: \
+ case AND: \
+ case IOR: \
+ case XOR: \
+ case ASHIFT: \
+ case ASHIFTRT: \
+ case LSHIFTRT: \
+ return COSTS_N_INSNS (1); \
+ case MULT: \
+ return COSTS_N_INSNS (GET_MODE_CLASS (GET_MODE (RTX)) == MODE_FLOAT \
+ || TARGET_MPYI ? 1 : 14); \
+ case DIV: \
+ case UDIV: \
+ case MOD: \
+ case UMOD: \
+ return COSTS_N_INSNS (GET_MODE_CLASS (GET_MODE (RTX)) == MODE_FLOAT \
+ ? 15 : 50);
+
+/* Compute the cost of computing a constant rtl expression RTX
+ whose rtx-code is CODE. The body of this macro is a portion
+ of a switch statement. If the code is computed here,
+ return it with a return statement. Otherwise, break from the switch.
+
+ An insn is assumed to cost 4 units.
+ COSTS_N_INSNS (N) is defined as (N) * 4 - 2.
+
+ Some small integers are effectively free for the C40. We should
+ also consider if we are using the small memory model. With
+ the big memory model we require an extra insn for a constant
+ loaded from memory.
+
+ This is used by expand_binop to decide whether to force a constant
+ into a register. If the cost is greater than 2 and the constant
+ is used within a short loop, it gets forced into a register.
+ Ideally, there should be some weighting as to how mnay times it is used
+ within the loop. */
+
+#define SHIFT_CODE_P(C) ((C) == ASHIFT || (C) == ASHIFTRT || (C) == LSHIFTRT)
+
+#define LOGICAL_CODE_P(C) ((C) == NOT || (C) == AND \
+ || (C) == IOR || (C) == XOR)
+
+#define NON_COMMUTATIVE_CODE_P ((C) == MINUS || (C) == COMPARE)
+
+#define CONST_COSTS(RTX,CODE,OUTER_CODE) \
+ case CONST_INT: \
+ if (c4x_J_constant (RTX)) \
+ return 0; \
+ if (! TARGET_C3X \
+ && OUTER_CODE == AND \
+ && GET_CODE (RTX) == CONST_INT \
+ && (INTVAL (RTX) == 255 || INTVAL (RTX) == 65535)) \
+ return 0; \
+ if (! TARGET_C3X \
+ && (OUTER_CODE == ASHIFTRT || OUTER_CODE == LSHIFTRT) \
+ && GET_CODE (RTX) == CONST_INT \
+ && (INTVAL (RTX) == 16 || INTVAL (RTX) == 24)) \
+ return 0; \
+ if (TARGET_C3X && SHIFT_CODE_P (OUTER_CODE)) \
+ return 3; \
+ if (LOGICAL_CODE_P (OUTER_CODE) \
+ ? c4x_L_constant (RTX) : c4x_I_constant (RTX)) \
+ return 2; \
+ case CONST: \
+ case LABEL_REF: \
+ case SYMBOL_REF: \
+ return 4; \
+ case CONST_DOUBLE: \
+ if (c4x_H_constant (RTX)) \
+ return 2; \
+ if (GET_MODE (RTX) == QFmode) \
+ return 4; \
+ else \
+ return 8;
+
+/* Compute the cost of an address. This is meant to approximate the size
+ and/or execution delay of an insn using that address. If the cost is
+ approximated by the RTL complexity, including CONST_COSTS above, as
+ is usually the case for CISC machines, this macro should not be defined.
+ For aggressively RISCy machines, only one insn format is allowed, so
+ this macro should be a constant. The value of this macro only matters
+ for valid addresses. We handle the most common address without
+ a call to c4x_address_cost. */
+
+extern int c4x_address_cost ();
+
+#define ADDRESS_COST(ADDR) (REG_P (ADDR) ? 1 : c4x_address_cost (ADDR))
+
+#define CANONICALIZE_COMPARISON(CODE, OP0, OP1) \
+if (REG_P (OP1) && ! REG_P (OP0)) \
+{ \
+ rtx tmp = OP0; OP0 = OP1 ; OP1 = tmp; \
+ CODE = swap_condition (CODE); \
+}
+
+#define EXT_CLASS_P(CLASS) (reg_class_subset_p (CLASS, EXT_REGS))
+#define ADDR_CLASS_P(CLASS) (reg_class_subset_p (CLASS, ADDR_REGS))
+#define INDEX_CLASS_P(CLASS) (reg_class_subset_p (CLASS, INDEX_REGS))
+#define EXPENSIVE_CLASS_P(CLASS) (ADDR_CLASS_P(CLASS) \
+ || INDEX_CLASS_P(CLASS) || (CLASS) == SP_REG)
+
+/* Compute extra cost of moving data between one register class
+ and another. */
+
+#define REGISTER_MOVE_COST(FROM, TO) 2
+
+/* Memory move cost is same as fast register move. Maybe this should
+ be bumped up? */
+
+#define MEMORY_MOVE_COST(M,C,I) 4
+
+/* Branches are kind of expensive (even with delayed branching) so
+ make their cost higher. */
+
+#define BRANCH_COST 8
+
+/* Adjust the cost of dependencies. */
+
+#define ADJUST_COST(INSN,LINK,DEP,COST) \
+ (COST) = c4x_adjust_cost (INSN, LINK, DEP, COST)
+
+#define WORD_REGISTER_OPERATIONS
+
+/* Dividing the Output into Sections. */
+
+#define TEXT_SECTION_ASM_OP "\t.text"
+
+#define DATA_SECTION_ASM_OP "\t.data"
+
+#define USE_CONST_SECTION 1
+
+#define CONST_SECTION_ASM_OP "\t.sect\t\".const\""
+
+/* Do not use .init section so __main will be called on startup. This will
+ call __do_global_ctors and prepare for __do_global_dtors on exit. */
+
+#if 0
+#define INIT_SECTION_ASM_OP "\t.sect\t\".init\""
+#endif
+
+#define FINI_SECTION_ASM_OP "\t.sect\t\".fini\""
+
+/* Support const sections and the ctors and dtors sections for g++.
+ Note that there appears to be two different ways to support const
+ sections at the moment. You can either #define the symbol
+ READONLY_DATA_SECTION (giving it some code which switches to the
+ readonly data section) or else you can #define the symbols
+ EXTRA_SECTIONS, EXTRA_SECTION_FUNCTIONS, SELECT_SECTION, and
+ SELECT_RTX_SECTION. We do both here just to be on the safe side. */
+
+/* Define a few machine-specific details of the implementation of
+ constructors.
+
+ The __CTORS_LIST__ goes in the .ctors section. Define CTOR_LIST_BEGIN
+ and CTOR_LIST_END to contribute to the .ctors section an instruction to
+ push a word containing 0 (or some equivalent of that).
+
+ Define ASM_OUTPUT_CONSTRUCTOR to push the address of the constructor. */
+
+#define CTORS_SECTION_ASM_OP "\t.sect\t\".ctors\""
+#define DTORS_SECTION_ASM_OP "\t.sect\t\".dtors\""
+
+/* Constructor list on stack is in reverse order. Go to the end of the
+ list and go backwards to call constructors in the right order. */
+
+#define DO_GLOBAL_CTORS_BODY \
+do { \
+ extern func_ptr __CTOR_LIST__[]; \
+ func_ptr *p, *beg = __CTOR_LIST__ + 1; \
+ for (p = beg; *p ; p++) ; \
+ while (p != beg) \
+ (*--p) (); \
+} while (0)
+
+/* The TI tooling uses atexit. */
+#define HAVE_ATEXIT
+
+#undef EXTRA_SECTIONS
+#define EXTRA_SECTIONS in_const, in_init, in_fini, in_ctors, in_dtors
+
+#undef EXTRA_SECTION_FUNCTIONS
+#define EXTRA_SECTION_FUNCTIONS \
+ CONST_SECTION_FUNCTION \
+ INIT_SECTION_FUNCTION \
+ FINI_SECTION_FUNCTION \
+ CTORS_SECTION_FUNCTION \
+ DTORS_SECTION_FUNCTION
+
+#define INIT_SECTION_FUNCTION \
+void \
+init_section () \
+{ \
+ if (in_section != in_init) \
+ { \
+ fprintf (asm_out_file, ";\t.init\n"); \
+ in_section = in_init; \
+ } \
+}
+
+#define FINI_SECTION_FUNCTION \
+void \
+fini_section () \
+{ \
+ if (in_section != in_fini) \
+ { \
+ fprintf (asm_out_file, "\t%s\n", FINI_SECTION_ASM_OP); \
+ in_section = in_fini; \
+ } \
+}
+
+#define READONLY_DATA_SECTION() const_section ()
+
+#define CONST_SECTION_FUNCTION \
+void \
+const_section () \
+{ \
+ extern void text_section(); \
+ if (! USE_CONST_SECTION) \
+ text_section(); \
+ else if (in_section != in_const) \
+ { \
+ fprintf (asm_out_file, "%s\n", CONST_SECTION_ASM_OP); \
+ in_section = in_const; \
+ } \
+}
+
+#define ASM_STABS_OP "\t.stabs"
+
+/* The ctors and dtors sections are not normally put into use
+ by EXTRA_SECTIONS and EXTRA_SECTION_FUNCTIONS as defined in svr3.h,
+ but it can't hurt to define these macros for whatever systems use them. */
+
+#define CTORS_SECTION_FUNCTION \
+void \
+ctors_section () \
+{ \
+ if (in_section != in_ctors) \
+ { \
+ fprintf (asm_out_file, "%s\n", CTORS_SECTION_ASM_OP); \
+ in_section = in_ctors; \
+ } \
+}
+
+#define DTORS_SECTION_FUNCTION \
+void \
+dtors_section () \
+{ \
+ if (in_section != in_dtors) \
+ { \
+ fprintf (asm_out_file, "%s\n", DTORS_SECTION_ASM_OP); \
+ in_section = in_dtors; \
+ } \
+}
+
+#define ASM_OUTPUT_SECTION_NAME(FILE, DECL, NAME, RELOC) \
+ fprintf (FILE, "\t.sect\t\"%s\"\n", NAME);
+
+/* This is machine-dependent because it needs to push something
+ on the stack. */
+
+/* A C statement (sans semicolon) to output an element in the table of
+ global constructors. */
+#define ASM_OUTPUT_CONSTRUCTOR(FILE,NAME) \
+ do { \
+ ctors_section (); \
+ fprintf (FILE, "\t.word\t "); \
+ assemble_name (FILE, NAME); \
+ fprintf (FILE, "\n"); \
+ } while (0)
+
+/* A C statement (sans semicolon) to output an element in the table of
+ global destructors. */
+#define ASM_OUTPUT_DESTRUCTOR(FILE,NAME) \
+ do { \
+ dtors_section (); \
+ fprintf (FILE, "\t.word\t "); \
+ assemble_name (FILE, NAME); \
+ fprintf (FILE, "\n"); \
+ } while (0)
+
+/* A C statement or statements to switch to the appropriate
+ section for output of DECL. DECL is either a `VAR_DECL' node
+ or a constant of some sort. RELOC indicates whether forming
+ the initial value of DECL requires link-time relocations. */
+
+#define SELECT_SECTION(DECL, RELOC) \
+{ \
+ if (TREE_CODE (DECL) == STRING_CST) \
+ { \
+ if (! flag_writable_strings) \
+ const_section (); \
+ else \
+ data_section (); \
+ } \
+ else if (TREE_CODE (DECL) == VAR_DECL) \
+ { \
+ if ((0 && RELOC) /* should be (flag_pic && RELOC) */ \
+ || ! TREE_READONLY (DECL) || TREE_SIDE_EFFECTS (DECL) \
+ || ! DECL_INITIAL (DECL) \
+ || (DECL_INITIAL (DECL) != error_mark_node \
+ && ! TREE_CONSTANT (DECL_INITIAL (DECL)))) \
+ data_section (); \
+ else \
+ const_section (); \
+ } \
+ else \
+ const_section (); \
+}
+
+/* A C statement or statements to switch to the appropriate
+ section for output of RTX in mode MODE. RTX is some kind
+ of constant in RTL. The argument MODE is redundant except
+ in the case of a `const_int' rtx. Currently, these always
+ go into the const section. */
+
+#define SELECT_RTX_SECTION(MODE, RTX) const_section()
+
+
+/* Overall Framework of an Assembler File */
+
+#define ASM_FILE_START(FILE) \
+{ \
+ int dspversion = 0; \
+ if (TARGET_C30) dspversion = 30; \
+ if (TARGET_C31) dspversion = 31; \
+ if (TARGET_C32) dspversion = 32; \
+ if (TARGET_C40) dspversion = 40; \
+ if (TARGET_C44) dspversion = 44; \
+ fprintf (FILE, "\t.version\t%d\n", dspversion); \
+ fprintf (FILE, "\t.file\t"); \
+ if (TARGET_TI) \
+ { \
+ char *p; \
+ char *after_dir = main_input_filename; \
+ for (p = main_input_filename; *p; p++) \
+ if (*p == '/') \
+ after_dir = p + 1; \
+ output_quoted_string (FILE, after_dir); \
+ } \
+ else \
+ output_quoted_string (FILE, main_input_filename); \
+ fprintf (FILE, "\n"); \
+}
+
+#define ASM_FILE_END(FILE) fprintf (FILE, "\t.end\n")
+
+/* We need to have a data section we can identify so that we can set
+ the DP register back to a data pointer in the small memory model.
+ This is only required for ISRs if we are paranoid that someone
+ may have quietly changed this register on the sly. */
+
+#define ASM_IDENTIFY_GCC(FILE) \
+ if (! TARGET_TI) fputs ("gcc2_compiled.:\n", FILE); \
+ fputs ("\t.data\ndata_sec:\n", FILE);
+
+#define ASM_COMMENT_START ";"
+
+#define ASM_APP_ON ""
+#define ASM_APP_OFF ""
+
+/* Output float/double constants QFmode. */
+
+#define ASM_OUTPUT_BYTE_FLOAT(FILE, VALUE) \
+{ long l; \
+ char str[30]; \
+ REAL_VALUE_TO_TARGET_SINGLE (VALUE, l); \
+ REAL_VALUE_TO_DECIMAL (VALUE, "%20lf", str); \
+ if (sizeof (int) == sizeof (long)) \
+ fprintf (FILE, "\t.word\t0%08xh\t; %s\n", l, str);\
+ else \
+ fprintf (FILE, "\t.word\t0%08lxh\t; %s\n", l, str);\
+}
+
+/* Output long double constants HFmode.
+ The first word contains the exponent and first part of the mantissa
+ in the same manner as QFmode. The second word contains the full
+ mantissa. We should ensure that the two words are allocated within
+ the same page for the large memory model since we only output a single
+ LDP instruction. FIXME. The simplest solution probably is to output
+ a LDP for each load. */
+
+#define ASM_OUTPUT_SHORT_FLOAT(FILE, VALUE) \
+{ long l[2]; \
+ char str[30]; \
+ REAL_VALUE_TO_TARGET_DOUBLE (VALUE, l); \
+ REAL_VALUE_TO_DECIMAL (VALUE, "%20lf", str); \
+ l[1] = (l[0] << 8) | ((l[1] >> 24) & 0xff); \
+ if (sizeof (int) == sizeof (long)) \
+ fprintf (FILE, "\t.word\t0%08xh\t; %s\n\t.word\t0%08xh\n", \
+ l[0], str, l[1]); \
+ else \
+ fprintf (FILE, "\t.word\t0%08lxh\t; %s\n\t.word\t0%08lxh\n", \
+ l[0], str, l[1]); \
+}
+
+#define ASM_OUTPUT_CHAR(FILE, VALUE) \
+{ fprintf (FILE, "\t.word\t"); \
+ output_addr_const (FILE, VALUE); \
+ if (GET_CODE (VALUE) != SYMBOL_REF) \
+ fprintf (FILE, " ; 0%08xh\n", INTVAL (VALUE)); \
+ else \
+ fputc ('\n', FILE); \
+}
+
+#define ASM_OUTPUT_BYTE(FILE, VALUE) \
+ fprintf (FILE, "\t.word\t0%xh\n", (VALUE))
+
+extern void c4x_output_ascii ();
+#define ASM_OUTPUT_ASCII(FILE, PTR, LEN) c4x_output_ascii (FILE, PTR, LEN)
+
+#define ASM_OPEN_PAREN "("
+#define ASM_CLOSE_PAREN ")"
+
+
+/* Output and Generation of Labels */
+
+#define NO_DOT_IN_LABEL /* Only required for TI format */
+
+#define ASM_OUTPUT_LABEL(FILE, NAME) \
+{ assemble_name (FILE, NAME); fputs (":\n", FILE); }
+
+#define ASM_GLOBALIZE_LABEL(FILE, NAME) \
+{ \
+ fprintf (FILE, "\t.global\t"); \
+ assemble_name (FILE, NAME); \
+ fputs ("\n", FILE); \
+}
+
+#define ASM_OUTPUT_EXTERNAL(FILE, DECL, NAME) \
+{ \
+ fprintf (FILE, "\t.ref\t"); \
+ assemble_name (FILE, NAME); \
+ fputc ('\n', FILE); \
+}
+
+/* A C statement to output on FILE an assembler pseudo-op to
+ declare a library function named external.
+ (Only needed to keep asm30 happy for ___divqf3 etc.) */
+
+#define ASM_OUTPUT_EXTERNAL_LIBCALL(FILE, FUN) \
+{ \
+ fprintf (FILE, "\t.ref\t"); \
+ assemble_name (FILE, XSTR (FUN, 0)); \
+ fprintf (FILE, "\n"); \
+}
+
+/* The prefix to add to user-visible assembler symbols. */
+
+#define USER_LABEL_PREFIX "_"
+
+/* This is how to output an internal numbered label where
+ PREFIX is the class of label and NUM is the number within the class. */
+
+#define ASM_OUTPUT_INTERNAL_LABEL(FILE, PREFIX, NUM) \
+asm_fprintf (FILE, "%s%d:\n", PREFIX, NUM)
+
+/* This is how to store into the string LABEL
+ the symbol_ref name of an internal numbered label where
+ PREFIX is the class of label and NUM is the number within the class.
+ This is suitable for output with `assemble_name'. */
+
+#define ASM_GENERATE_INTERNAL_LABEL(BUFFER, PREFIX, NUM) \
+ sprintf (BUFFER, "*%s%d", PREFIX, NUM)
+
+/* Store in OUTPUT a string (made with alloca) containing
+ an assembler-name for a local static variable named NAME.
+ LABELNO is an integer which is different for each call. */
+
+#define ASM_FORMAT_PRIVATE_NAME(OUTPUT, NAME, LABELNO) \
+( (OUTPUT) = (char *) alloca (strlen ((NAME)) + 10), \
+ sprintf ((OUTPUT), "%s%d", (NAME), (LABELNO)))
+
+
+/* Output of Dispatch Tables */
+
+/* This is how to output an element of a case-vector that is absolute. */
+
+#define ASM_OUTPUT_ADDR_VEC_ELT(FILE, VALUE) \
+ fprintf (FILE, "\t.long\tL%d\n", VALUE);
+
+/* This is how to output an element of a case-vector that is relative. */
+
+#define ASM_OUTPUT_ADDR_DIFF_ELT(FILE, BODY, VALUE, REL) \
+ fprintf (FILE, "\t.long\tL%d-L%d\n", VALUE, REL);
+
+#undef SIZE_TYPE
+#define SIZE_TYPE "unsigned int"
+
+#undef PTRDIFF_TYPE
+#define PTRDIFF_TYPE "int"
+
+#undef WCHAR_TYPE
+#define WCHAR_TYPE "long int"
+
+#undef WCHAR_TYPE_SIZE
+#define WCHAR_TYPE_SIZE 32
+
+#define INT_TYPE_SIZE 32
+#define LONG_LONG_TYPE_SIZE 64
+#define FLOAT_TYPE_SIZE 32
+#define DOUBLE_TYPE_SIZE 32
+#define LONG_DOUBLE_TYPE_SIZE 64 /* actually only 40 */
+
+/* Allow #sccs in preprocessor. */
+
+#define SCCS_DIRECTIVE
+
+/* Output #ident as a .ident. */
+
+#define ASM_OUTPUT_IDENT(FILE, NAME) \
+ fprintf (FILE, "\t.ident \"%s\"\n", NAME);
+
+#define CPP_PREDEFINES ""
+
+/* This says how to output an assembler line
+ to define a local common symbol. */
+
+#undef ASM_OUTPUT_LOCAL
+#define ASM_OUTPUT_LOCAL(FILE, NAME, SIZE, ROUNDED) \
+( fputs ("\t.bss\t", FILE), \
+ assemble_name (FILE, (NAME)), \
+ fprintf (FILE, ",%u\n", (ROUNDED)))
+
+/* Output of Uninitialized Variables */
+
+#undef ASM_OUTPUT_COMMON
+#define ASM_OUTPUT_COMMON(FILE, NAME, SIZE, ROUNDED) \
+( fputs ("\t.globl\t", FILE), \
+ assemble_name (FILE, (NAME)), \
+ fputs ("\n\t.bss\t", FILE), \
+ assemble_name (FILE, (NAME)), \
+ fprintf (FILE, ",%u\n", (ROUNDED)))
+
+/* Macros Controlling Initialization Routines */
+
+#define OBJECT_FORMAT_COFF
+#define REAL_NM_FILE_NAME "c4x-nm"
+
+/* Output of Assembler Instructions */
+
+/* Register names when used for integer modes. */
+
+#define REGISTER_NAMES \
+{ \
+ "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", \
+ "ar0", "ar1", "ar2", "ar3", "ar4", "ar5", "ar6", "ar7", \
+ "dp", "ir0", "ir1", "bk", "sp", "st", "die", "iie", \
+ "iif", "rs", "re", "rc", "r8", "r9", "r10", "r11" \
+}
+
+/* Alternate register names when used for floating point modes. */
+
+#define FLOAT_REGISTER_NAMES \
+{ \
+ "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7", \
+ "ar0", "ar1", "ar2", "ar3", "ar4", "ar5", "ar6", "ar7", \
+ "dp", "ir0", "ir1", "bk", "sp", "st", "die", "iie", \
+ "iif", "rs", "re", "rc", "f8", "f9", "f10", "f11" \
+}
+
+
+extern void c4x_print_operand ();
+#define PRINT_OPERAND(FILE, X, CODE) c4x_print_operand(FILE, X, CODE)
+
+/* Determine which codes are valid without a following integer. These must
+ not be alphabetic. */
+
+#define PRINT_OPERAND_PUNCT_VALID_P(CODE) ((CODE) == '#')
+
+extern void c4x_print_operand_address ();
+#define PRINT_OPERAND_ADDRESS(FILE, X) c4x_print_operand_address(FILE, X)
+
+/* Define this macro if you want to implement any pragmas. If defined, it
+ should be a C expression to be executed when #pragma is seen. The
+ argument STREAM is the stdio input stream from which the source
+ text can be read. CH is the first character after the #pragma. The
+ result of the expression is the terminating character found
+ (newline or EOF). */
+extern int c4x_handle_pragma ();
+#define HANDLE_PRAGMA(GETC, UNGETC, NAME) \
+ c4x_handle_pragma (GETC, UNGETC, NAME)
+
+extern void c4x_set_default_attributes ();
+#define SET_DEFAULT_DECL_ATTRIBUTES(DECL, ATTRIBUTES) \
+ c4x_set_default_attributes (DECL, &ATTRIBUTES)
+
+extern int c4x_valid_type_attribute_p ();
+#define VALID_MACHINE_TYPE_ATTRIBUTE(TYPE, ATTRIBUTES, NAME, ARGS) \
+ (c4x_valid_type_attribute_p (TYPE, ATTRIBUTES, NAME, ARGS))
+
+/* Assembler Commands for Alignment */
+
+#define ASM_OUTPUT_SKIP(FILE, SIZE) \
+{ int c = SIZE; \
+ for (; c > 0; --c) \
+ fprintf (FILE,"\t.word\t0\n"); \
+}
+
+#define ASM_NO_SKIP_IN_TEXT 1
+
+/* I'm not sure about this one. FIXME. */
+
+#define ASM_OUTPUT_ALIGN(FILE, LOG) \
+ if ((LOG) != 0) \
+ fprintf (FILE, "\t.align\t%d\n", (1 << (LOG)))
+
+
+/* Macros for SDB and DWARF Output (use .sdef instead of .def
+ to avoid conflict with TI's use of .def) */
+
+#define SDB_DELIM "\n"
+#define SDB_DEBUGGING_INFO
+
+#define PUT_SDB_DEF(A) \
+do { fprintf (asm_out_file, "\t.sdef\t"); \
+ ASM_OUTPUT_LABELREF (asm_out_file, A); \
+ fprintf (asm_out_file, SDB_DELIM); } while (0)
+
+#define PUT_SDB_PLAIN_DEF(A) \
+ fprintf (asm_out_file,"\t.sdef\t.%s%s", A, SDB_DELIM)
+
+#define PUT_SDB_BLOCK_START(LINE) \
+ fprintf (asm_out_file, \
+ "\t.sdef\t.bb%s\t.val\t.%s\t.scl\t100%s\t.line\t%d%s\t.endef\n", \
+ SDB_DELIM, SDB_DELIM, SDB_DELIM, (LINE), SDB_DELIM)
+
+#define PUT_SDB_BLOCK_END(LINE) \
+ fprintf (asm_out_file, \
+ "\t.sdef\t.eb%s\t.val\t.%s\t.scl\t100%s\t.line\t%d%s\t.endef\n", \
+ SDB_DELIM, SDB_DELIM, SDB_DELIM, (LINE), SDB_DELIM)
+
+#define PUT_SDB_FUNCTION_START(LINE) \
+ fprintf (asm_out_file, \
+ "\t.sdef\t.bf%s\t.val\t.%s\t.scl\t101%s\t.line\t%d%s\t.endef\n", \
+ SDB_DELIM, SDB_DELIM, SDB_DELIM, (LINE), SDB_DELIM)
+
+#define PUT_SDB_FUNCTION_END(LINE) \
+ fprintf (asm_out_file, \
+ "\t.sdef\t.ef%s\t.val\t.%s\t.scl\t101%s\t.line\t%d%s\t.endef\n", \
+ SDB_DELIM, SDB_DELIM, SDB_DELIM, (LINE), SDB_DELIM)
+
+#define PUT_SDB_EPILOGUE_END(NAME) \
+do { fprintf (asm_out_file, "\t.sdef\t"); \
+ ASM_OUTPUT_LABELREF (asm_out_file, NAME); \
+ fprintf (asm_out_file, \
+ "%s\t.val\t.%s\t.scl\t-1%s\t.endef\n", \
+ SDB_DELIM, SDB_DELIM, SDB_DELIM); } while (0)
+
+
+/* Define results of standard character escape sequences. */
+
+#define TARGET_BELL 007
+#define TARGET_BS 010
+#define TARGET_TAB 011
+#define TARGET_NEWLINE 012
+#define TARGET_VT 013
+#define TARGET_FF 014
+#define TARGET_CR 015
+
+/* This is the kind of divide that is easiest to do in the general case. */
+
+#define EASY_DIV_EXPR TRUNC_DIV_EXPR
+
+/* Define this as 1 if `char' should by default be signed; else as 0. */
+
+#define DEFAULT_SIGNED_CHAR 1
+
+/* A function address in a call instruction is a byte address (for
+ indexing purposes) so give the MEM rtx a byte's mode. */
+
+#define FUNCTION_MODE QImode
+
+#define SLOW_BYTE_ACCESS 0
+
+/* Specify the machine mode that pointers have. After generation of
+ RTL, the compiler makes no further distinction between pointers and
+ any other objects of this machine mode. */
+
+#define Pmode QImode
+
+/* On the C4x we can write the following code. We have to clear the cache
+ every time we execute it because the data in the stack could change.
+
+ laj $+4
+ addi3 4,r11,ar0
+ lda *ar0,ar1
+ lda *+ar0(1),ar0
+ bud ar1
+ nop
+ nop
+ or 1000h,st
+ .word FNADDR
+ .word CXT
+
+ On the c3x this is a bit more difficult. We have to write self
+ modifying code here. So we have to clear the cache every time
+ we execute it because the data in the stack could change.
+
+ ldiu TOP_OF_FUNCTION,ar1
+ lsh 16,ar1
+ or BOTTOM_OF_FUNCTION,ar1
+ ldiu TOP_OF_STATIC,ar0
+ bud ar1
+ lsh 16,ar0
+ or BOTTOM_OF_STATIC,ar0
+ or 1000h,st
+
+ */
+
+#define TRAMPOLINE_SIZE (TARGET_C3X ? 8 : 10)
+
+#define TRAMPOLINE_TEMPLATE(FILE) \
+{ \
+ if (TARGET_C3X) \
+ { \
+ asm_fprintf (FILE, "\tldiu\t0,ar1\n"); \
+ asm_fprintf (FILE, "\tlsh\t16,ar1\n"); \
+ asm_fprintf (FILE, "\tor\t0,ar1\n"); \
+ asm_fprintf (FILE, "\tldiu\t0,ar0\n"); \
+ asm_fprintf (FILE, "\tbud\tar1\n"); \
+ asm_fprintf (FILE, "\tlsh\t16,ar0\n"); \
+ asm_fprintf (FILE, "\tor\t0,ar0\n"); \
+ asm_fprintf (FILE, "\tor\t1000h,st\n"); \
+ } \
+ else \
+ { \
+ asm_fprintf (FILE, "\tlaj\t$+4\n"); \
+ asm_fprintf (FILE, "\taddi3\t4,r11,ar0\n"); \
+ asm_fprintf (FILE, "\tlda\t*ar0,ar1\n"); \
+ asm_fprintf (FILE, "\tlda\t*+ar0(1),ar0\n"); \
+ asm_fprintf (FILE, "\tbud\tar1\n"); \
+ asm_fprintf (FILE, "\tnop\n"); \
+ asm_fprintf (FILE, "\tnop\n"); \
+ asm_fprintf (FILE, "\tor\t1000h,st\n"); \
+ asm_fprintf (FILE, "\t.word\t0\n"); \
+ asm_fprintf (FILE, "\t.word\t0\n"); \
+ } \
+}
+
+#define INITIALIZE_TRAMPOLINE(TRAMP, FNADDR, CXT) \
+{ \
+ if (TARGET_C3X) \
+ { \
+ rtx tmp1, tmp2; \
+ tmp1 = expand_shift (RSHIFT_EXPR, QImode, FNADDR, \
+ size_int (16), 0, 1); \
+ tmp2 = expand_shift (LSHIFT_EXPR, QImode, \
+ gen_rtx (CONST_INT, VOIDmode, 0x5069), \
+ size_int (16), 0, 1); \
+ emit_insn (gen_iorqi3 (tmp1, tmp1, tmp2)); \
+ emit_move_insn (gen_rtx (MEM, QImode, \
+ plus_constant (tramp, 0)), tmp1); \
+ tmp1 = expand_and (FNADDR, gen_rtx (CONST_INT, VOIDmode, \
+ 0xffff), 0); \
+ tmp2 = expand_shift (LSHIFT_EXPR, QImode, \
+ gen_rtx (CONST_INT, VOIDmode, 0x1069), \
+ size_int (16), 0, 1); \
+ emit_insn (gen_iorqi3 (tmp1, tmp1, tmp2)); \
+ emit_move_insn (gen_rtx (MEM, QImode, \
+ plus_constant (tramp, 2)), tmp1); \
+ tmp1 = expand_shift (RSHIFT_EXPR, QImode, CXT, \
+ size_int (16), 0, 1); \
+ tmp2 = expand_shift (LSHIFT_EXPR, QImode, \
+ gen_rtx (CONST_INT, VOIDmode, 0x5068), \
+ size_int (16), 0, 1); \
+ emit_insn (gen_iorqi3 (tmp1, tmp1, tmp2)); \
+ emit_move_insn (gen_rtx (MEM, QImode, \
+ plus_constant (tramp, 3)), tmp1); \
+ tmp1 = expand_and (CXT, gen_rtx (CONST_INT, VOIDmode, \
+ 0xffff), 0); \
+ tmp2 = expand_shift (LSHIFT_EXPR, QImode, \
+ gen_rtx (CONST_INT, VOIDmode, 0x1068), \
+ size_int (16), 0, 1); \
+ emit_insn (gen_iorqi3 (tmp1, tmp1, tmp2)); \
+ emit_move_insn (gen_rtx (MEM, QImode, \
+ plus_constant (tramp, 6)), tmp1); \
+ } \
+ else \
+ { \
+ emit_move_insn (gen_rtx (MEM, QImode, \
+ plus_constant (TRAMP, 8)), FNADDR); \
+ emit_move_insn (gen_rtx (MEM, QImode, \
+ plus_constant (TRAMP, 9)), CXT); \
+ } \
+}
+
+/* Specify the machine mode that this machine uses for the index in
+ the tablejump instruction. */
+
+#define CASE_VECTOR_MODE Pmode
+
+/* Max number of (32-bit) bytes we can move from memory to memory
+ in one reasonably fast instruction. */
+
+#define MOVE_MAX 1
+
+/* MOVE_RATIO is the number of move instructions that is better than a
+ block move. */
+
+#define MOVE_RATIO 2 /* Default value */
+
+#define BSS_SECTION_ASM_OP ".bss"
+
+#define ASM_OUTPUT_REG_PUSH(FILE, REGNO) \
+ asm_fprintf (FILE, "\tpush\t%s\n", reg_names[REGNO])
+
+/* This is how to output an insn to pop a register from the stack.
+ It need not be very fast code. */
+
+#define ASM_OUTPUT_REG_POP(FILE, REGNO) \
+ asm_fprintf (FILE, "\tpop\t%s\n", reg_names[REGNO])
+
+/* 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
+
+#define DBX_REGISTER_NUMBER(REGNO) (REGNO)
+
+/* We need to use direct addressing for large constants and addresses
+ that cannot fit within an instruction. We must check for these
+ after after the final jump optimisation pass, since this may
+ introduce a local_move insn for a SYMBOL_REF. This pass
+ must come before delayed branch slot filling since it can generate
+ additional instructions. */
+
+#define MACHINE_DEPENDENT_REORG(INSNS) c4x_process_after_reload(INSNS)
+
+#define DBR_OUTPUT_SEQEND(FILE) \
+if (final_sequence != NULL_RTX) \
+{ \
+ int count; \
+ int laj = GET_CODE (XEXP (XEXP (final_sequence, 0), 0)) == CALL_INSN; \
+ \
+ count = dbr_sequence_length(); \
+ while (count < (laj ? 2 : 3)) \
+ { \
+ fputs("\tnop\n", FILE); \
+ count++; \
+ } \
+ if (laj) \
+ fputs("\tpush\tr11\n", FILE); \
+}
+
+#define NO_FUNCTION_CSE
+
+/* We don't want a leading tab. */
+
+#define ASM_OUTPUT_ASM(FILE, STRING) fprintf (FILE, "%s\n", STRING)
+
+/* Define the codes that are matched by predicates in c4x.c. */
+
+#define PREDICATE_CODES \
+ {"fp_zero_operand", {CONST_DOUBLE}}, \
+ {"const_operand", {CONST_INT, CONST_DOUBLE}}, \
+ {"stik_const_operand", {CONST_INT}}, \
+ {"not_const_operand", {CONST_INT}}, \
+ {"reg_operand", {REG, SUBREG}}, \
+ {"reg_or_const_operand", {REG, SUBREG, CONST_INT, CONST_DOUBLE}},\
+ {"r0r1_reg_operand", {REG, SUBREG}}, \
+ {"r2r3_reg_operand", {REG, SUBREG}}, \
+ {"ext_low_reg_operand", {REG, SUBREG}}, \
+ {"ext_reg_operand", {REG, SUBREG}}, \
+ {"std_reg_operand", {REG, SUBREG}}, \
+ {"addr_reg_operand", {REG, SUBREG}}, \
+ {"index_reg_operand", {REG, SUBREG}}, \
+ {"dp_reg_operand", {REG}}, \
+ {"sp_reg_operand", {REG}}, \
+ {"st_reg_operand", {REG}}, \
+ {"rc_reg_operand", {REG}}, \
+ {"call_address_operand", {REG, SYMBOL_REF, LABEL_REF, CONST}}, \
+ {"src_operand", {SUBREG, REG, MEM, CONST_INT, CONST_DOUBLE}}, \
+ {"src_hi_operand", {SUBREG, REG, MEM, CONST_DOUBLE}}, \
+ {"lsrc_operand", {SUBREG, REG, MEM, CONST_INT, CONST_DOUBLE}}, \
+ {"tsrc_operand", {SUBREG, REG, MEM, CONST_INT, CONST_DOUBLE}}, \
+ {"any_operand", {SUBREG, REG, MEM, CONST_INT, CONST_DOUBLE}}, \
+ {"par_ind_operand", {MEM}}, \
+ {"parallel_operand", {SUBREG, REG, MEM}}, \
+ {"symbolic_address_operand", {SYMBOL_REF, LABEL_REF, CONST}}, \
+ {"mem_operand", {MEM}},
+
+
+/* Variables in c4x.c */
+
+extern enum reg_class c4x_regclass_map[];/* smallest class containing REGNO */
+extern enum machine_mode c4x_caller_save_map[];
+
+extern struct rtx_def *c4x_compare_op0; /* operand 0 for comparisons */
+extern struct rtx_def *c4x_compare_op1; /* operand 1 for comparisons */
+
+extern int c4x_rpts_cycles; /* max cycles for RPTS */
+extern int c4x_cpu_version; /* cpu version C30/31/32/40/44 */
+
+/* Functions in c4x.c */
+
+extern void c4x_function_prologue ();
+
+extern void c4x_function_epilogue ();
+
+extern struct rtx_def *c4x_operand_subword ();
+
+extern struct rtx_def *c4x_adj_offsettable_operand ();
+
+extern char *c4x_output_cbranch ();
+
+extern int c4x_null_epilogue_p ();
+
+extern int c4x_autoinc_operand ();
+
+extern int c4x_label_conflict ();
+
+extern int c4x_address_conflict ();
+
+extern int c4x_adjust_cost ();
+
+extern void c4x_process_after_reload ();
+
+extern void c4x_combine_parallel ();
+
+extern int c4x_rptb_nop_p ();
+
+extern int c4x_rptb_rpts_p ();
+
+extern int fp_zero_operand ();
+
+extern int const_operand ();
+
+extern int stik_const_operand ();
+
+extern int not_const_operand ();
+
+extern int parallel_operand ();
+
+extern int reg_or_const_operand ();
+
+extern int reg_operand ();
+
+extern int mixed_subreg_operand ();
+
+extern int reg_imm_operand ();
+
+extern int r0r1_reg_operand ();
+
+extern int r2r3_reg_operand ();
+
+extern int ext_low_reg_operand ();
+
+extern int ext_reg_operand ();
+
+extern int std_reg_operand ();
+
+extern int src_operand ();
+
+extern int src_hi_operand ();
+
+extern int lsrc_operand ();
+
+extern int tsrc_operand ();
+
+extern int addr_reg_operand ();
+
+extern int index_reg_operand ();
+
+extern int dp_reg_operand ();
+
+extern int sp_reg_operand ();
+
+extern int rc_reg_operand ();
+
+extern int st_reg_operand ();
+
+extern int symbolic_address_operand ();
+
+extern int ar0_reg_operand ();
+
+extern int ar0_mem_operand ();
+
+extern int ar1_reg_operand ();
+
+extern int ar1_mem_operand ();
+
+extern int ar2_reg_operand ();
+
+extern int ar2_mem_operand ();
+
+extern int ar3_reg_operand ();
+
+extern int ar3_mem_operand ();
+
+extern int ar4_reg_operand ();
+
+extern int ar4_mem_operand ();
+
+extern int ar5_reg_operand ();
+
+extern int ar5_mem_operand ();
+
+extern int ar6_reg_operand ();
+
+extern int ar6_mem_operand ();
+
+extern int ar7_reg_operand ();
+
+extern int ar7_mem_operand ();
+
+extern int ir0_reg_operand ();
+
+extern int ir0_mem_operand ();
+
+extern int ir1_reg_operand ();
+
+extern int ir1_mem_operand ();
+
+extern int group1_reg_operand ();
+
+extern int group1_mem_operand ();
+
+extern int arx_reg_operand ();
+
+extern int call_address_operand ();
+
+extern int par_ind_operand ();
+
+extern int not_rc_reg ();
+
+extern int not_modify_reg ();
+
+extern int c4x_H_constant ();
+
+extern int c4x_I_constant ();
+
+extern int c4x_J_constant ();
+
+extern int c4x_L_constant ();
+
+extern int c4x_Q_constraint ();
+
+extern int c4x_R_constraint ();
+
+extern int c4x_S_constraint ();
+
+extern int c4x_T_constraint ();
+
+extern int c4x_U_constraint ();
+
+extern void c4x_emit_libcall ();
+
+extern void c4x_emit_libcall3 ();
+
+extern void c4x_emit_libcall_mulhi ();
+
+extern int c4x_emit_move_sequence ();
+
+extern int legitimize_operands ();
+
+extern int valid_operands ();
+
+extern int valid_parallel_load_store ();
+
+extern int valid_parallel_operands_4 ();
+
+extern int valid_parallel_operands_5 ();
+
+extern int valid_parallel_operands_6 ();
diff --git a/gnu/egcs/gcc/config/c4x/c4x.md b/gnu/egcs/gcc/config/c4x/c4x.md
new file mode 100644
index 00000000000..a0a4e0b9dd9
--- /dev/null
+++ b/gnu/egcs/gcc/config/c4x/c4x.md
@@ -0,0 +1,7187 @@
+;; Machine description for the TMS320C[34]x for GNU C compiler
+;; Copyright (C) 1994-98, 1999 Free Software Foundation, Inc.
+
+;; Contributed by Michael Hayes (m.hayes@elec.canterbury.ac.nz)
+;; and Herman Ten Brugge (Haj.Ten.Brugge@net.HCC.nl)
+
+;; 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.
+
+;
+; TODO :
+; Try using PQImode again for addresses since C30 only uses
+; 24-bit addresses. Ideally GCC would emit different insns
+; for QImode and Pmode, whether Pmode was QImode or PQImode.
+; For addresses we wouldn't have to have a clobber of the CC
+; associated with each insn and we could use MPYI in address
+; calculations without having to synthesise a proper 32 bit multiply.
+
+; Additional C30/C40 instructions not coded:
+; CALLcond, IACK, IDLE, LDE, LDFI, LDII, LDM, NORM, RETIcond
+; ROLC, RORC, SIGI, STFI, STII, SUBC, SWI, TRAPcond
+
+; Additional C40 instructions not coded:
+; LDEP, LDPE, LWRct, FRIEEE, TOIEEE, LAJcond, LATcond, RETIcondD
+
+;
+; C4x MODES
+;
+; QImode char, short, int, long (32-bits)
+; HImode long long (64-bits)
+; QFmode float, double (32-bits)
+; HFmode long double (40-bits)
+; CCmode
+; CC_NOOVmode
+
+;
+; C4x PREDICATES:
+;
+; comparison_operator LT, GT, LE, GE, LTU, GTU, LEU, GEU, EQ, NE
+; memory_operand memory [m]
+; immediate_operand immediate constant [IKN]
+; register_operand register [rf]
+; general_operand register, memory, constant [rfmI]
+
+; addr_reg_operand AR0-AR7, pseudo reg [a]
+; sp_reg_operand SP [b]
+; std_reg_operand AR0-AR7, IR0-IR1, RC, RS, RE, SP, pseudo [c]
+; ext_reg_operand R0-R11, pseudo reg [f]
+; ext_low_reg_operand R0-R7, pseudo reg [q]
+; index_reg_operand IR0-IR1, pseudo reg [x]
+; st_reg_operand ST [y]
+; dp_reg_operand DP [z]
+; stik_const_operand 5-bit const [K]
+; src_operand general operand [rfHmI]
+; par_ind_operand indirect S mode (ARx + 0, 1, IRx) [S<>]
+; parallel_operand par_ind_operand or ext_low_reg_operand
+
+; ADDI src2, src1, dst three operand op
+; ADDI src, dst two operand op
+
+; Note that the predicates are only used when selecting a pattern
+; to determine if an operand is valid.
+
+; The constraints then select which of the possible valid operands
+; is present (and guide register selection). The actual assembly
+; instruction is then selected on the basis of the constraints.
+
+; The extra constraint (valid_operands) is used to determine if
+; the combination of operands is legitimate for the pattern.
+
+;
+; C4x CONSTRAINTS:
+;
+; a address reg AR0-AR7
+; b stack pointer SP
+; c other int reg AR0-AR7, IR0-IR1, RC, RS, RE
+; d fp reg R0-R11 (sets CC when dst)
+; e
+; f fp reg R0-R11 (sets CC when dst)
+; g general reg, memory, constant
+; h fp reg (HFmode) R0-R11 (sets CC when dst)
+; i immediate int constant
+; j
+; k block count BK
+; l
+; m memory
+; n immediate int constant with known numeric value
+; o offsettable memory
+; p memory address
+; q low fp reg R0-R7 (sets CC when dst)
+; r general reg R0-R11, AR0-AR7, IR0-IR1, RC, RS, RE
+; s immediate int constant (value not explicit)
+; t R0-R1
+; u R2-R3
+; v repeat count reg RC
+; w
+; x index reg IR0-IR1
+; y status (CC) reg ST
+; z data pointer DP
+
+; G fp zero
+; H fp 16-bit constant
+; I signed 16-bit
+; J signed 8-bit (C4x only)
+; K signed 5-bit (C4x only)
+; L unsigned 16-bit
+; M unsigned 8-bit (C4x only)
+; N ones complement of unsigned 16-bit
+; O 16 bit high constant
+; Q ARx + 9-bit signed disp
+; R ARx + 5-bit unsigned disp (C4x only)
+; S ARx + 0, 1, IRx disp
+; T direct memory operand
+; V non offsettable memory
+; X any operand
+; < memory operand with autodecrement addressing
+; > memory operand with autoincrement addressing
+; { memory operand with pre-modify addressing
+; } memory operand with post-modify addressing
+
+; Note that the 'd', 'f', and 'h' constraints are equivalent.
+; The m constraint is equivalent to 'QT<>{}'
+
+; Note we cannot use the 'g' constraint with Pmode (i.e, QImode)
+; operations since LEGITIMATE_CONSTANT_P accepts SYMBOL_REF.
+; So instead we use 'rIm' for signed operands or 'rLm' for unsigned operands.
+
+; Note that the constraints are used to select the operands
+; for a chosen pattern. The constraint that requires the fewest
+; instructions to load an operand is chosen.
+
+; Note that the 'r' constraint is mostly only used for src integer register
+; operands, while 'c' and 'd' constraints are generally only used for dst
+; integer register operands (the 'r' constraint is the union of the 'c' and
+; 'd' constraints). When a register satisfying the 'd' constraint
+; is used as a dst operand, the CC gets clobbered (except for LDIcond)---but
+; not for 'c'.
+
+; The 'f' constraint is only for float register operands---when
+; a register satisying the 'f' constraint is used as a dst operand,
+; the CC gets clobbered (except for LDFcond).
+
+; The ! in front of the 'b' constaint says to GCC to disparage the
+; use of this constraint. The 'b' constraint applies only to the SP.
+
+; Note that we deal with the condition code CC like some of the RISC
+; architectures (arm, sh, sparc) where it is stored in a general register,
+; in this case the hard register ST (21). Unlike these other architectures
+; that do not set the CC with many instructions, the C[34]x architectures
+; sets the CC for many instructions when the destination register is
+; an extended precision register. While it would have been easier
+; to use the generic cc0 register to store the CC, as with most of
+; the other ported architectures, this constrains the setting and testing
+; of the CC to be consecutive insns. Thus we would reduce the benefit
+; of scheduling instructions to avoid pipeline conflicts and filling of
+; delayed branch slots.
+
+; Since the C[34]x has many instructions that set the CC, we pay the
+; price of having to explicity define which insns clobber the CC
+; (rather than using the macro NOTICE_UPDATE_CC).
+
+; Note that many patterns say that the CC is clobbered when in fact
+; that it may not be (depending on the destination register).
+; We have to cover ourselves if an extended precision register
+; is allocated to the destination register.
+; Unfortunately, it is not easy to tell GCC that the clobbering of CC
+; is register dependent. If we could tolerate the ST register being
+; copied about, then we could store the CC in a pseudo register and
+; use constructs such as (clobber (match_scratch:CC N "&y,X")) to
+; indicate that the 'y' class (ST register) is clobbered for the
+; first combination of operands, but not with the second.
+; I tried this approach for a while but reload got unhappy since I
+; didn't allow it to move the CC around.
+
+; Note that fundamental operations, such as moves, must not clobber the
+; CC. Thus movqi choses a move instruction that doesn't clobber the CC.
+; If GCC wants to combine a move with a compare, it is smart enough to
+; chose the move instruction that sets the CC.
+
+; Unfortunately, the C[34]x instruction set does not have arithmetic or
+; logical operations that never touch the CC. We thus have to assume
+; that the CC may be clobbered at all times. If we define patterns
+; such as addqi without the clobber of CC, then GCC will be forced
+; to use registers such as the auxiliary registers which can cause
+; horrible pipeline conflicts. The tradeoff is that GCC can't now
+; sneak in an add instruction between setting and testing of the CC.
+
+; Most of the C[34]x instructions require operands of the following formats,
+; where imm represents an immediate constant, dir a direct memory reference,
+; ind an indirect memory reference, and reg a register:
+
+; src2 (op2) src1 (op1) dst (op0)
+; imm dir ind reg | imm dir ind reg | reg Notes
+;---------------------+----------------------+------
+; ILH T Q<> r | - - - 0 | r 2 operand
+; - - S<> r | - - S<> r | r
+; J - R - | - - R r | r C4x
+
+; Arithmetic operations use the I, J constraints for immediate constants,
+; while logical operations use the L, J constraints. Floating point
+; operations use the H constraint for immediate constants.
+
+; With most instructions the src2 and src1 operands are commutative
+; (except for SUB, SUBR, ANDN). The assembler considers
+; ADDI 10, R0, R1 and ADDI R0, 10, R1 to be equivalent.
+; We thus match src2 and src1 with the src_operand predicate and
+; use valid_operands as the extra constraint to reject invalid
+; operand combinations. For example, ADDI @foo, @bar, R0.
+
+; Note that we use the ? modifier so that reload doesn't preferentially
+; try the alternative where three registers are acceptable as
+; operands (whenever an operand requires reloading). Instead it will try
+; the 2 operand form which will produce better code since it won't require
+; a new spill register.
+
+; Note that the floating point representation of 0.0 on the C4x
+; is 0x80000000 (-2147483648). This value produces an warning
+; message on 32-bit machines about the decimal constant being so large
+; that it is unsigned.
+
+; With two operand instructions patterns having two sets,
+; the compare set must come first to keep the combiner happy.
+; While the combiner seems to cope most of the time with the
+; compare set coming second, it's best to have it first.
+
+;
+; C4x CONSTANT attributes
+;
+(define_attr "cpu" "c4x,c3x"
+ (const
+ (cond [(symbol_ref "TARGET_C3X") (const_string "c3x")]
+ (const_string "c4x"))))
+
+;
+; C4x INSN ATTRIBUTES:
+;
+; lda load address, non-clobber CC
+; store memory store, non-clobber CC
+; load_load parallel memory loads, non-clobber CC
+; load_store parallel memory load and store, non-clobber CC
+; store_load parallel memory store and load, non-clobber CC
+; store_store parallel memory stores, non-clobber CC
+; unary two operand arithmetic, non-clobber CC
+; unarycc two operand arithmetic, clobber CC
+; binary three operand arithmetic, non-clobber CC
+; binarycc three operand arithmetic, clobber CC
+; compare compare, clobber CC
+; call function call
+; rets return from subroutine
+; jump unconditional branch
+; jmpc conditional branch
+; db decrement and branch (unconditional)
+; dbc decrement and branch (conditional)
+; ldp load DP
+; push stack push
+; pop stack pop
+; repeat block repeat
+; repeat_top block repeat top
+; laj link and jump
+; multi multiple instruction
+; misc nop (default)
+
+; The only real instructions that affect things are the ones that modify
+; address registers and ones that call or jump. Note that the number
+; of operands refers to the RTL insn pattern, not the number of explicit
+; operands in the machine instruction.
+;
+(define_attr "type" "lda,store,unary,unarycc,binary,binarycc,compare,call,rets,jump,jmpc,db,dbc,misc,ldp,repeat,repeat_top,laj,load_load,load_store,store_load,store_store,push,pop,multi"
+ (const_string "misc"))
+
+
+; Some instructions operate on unsigned data constants, some on signed data
+; constants, or the ones complement of unsigned constants.
+; This differentiates them. Default to signed. This attribute
+; is used by the macro SMALL_CONST () (defined in c4x.h) to determine
+; whether an immediate integer constant will fit within the instruction,
+; or will have to be loaded using direct addressing from memory.
+; Note that logical operations assume unsigned integers whereas
+; arithmetic operations assume signed integers. Note that the C4x
+; small immediate constant (J) used as src2 in three operand instructions
+; is always signed. not_uint16 refers to a number that fits into 16-bits
+; when one's complemented.
+;
+(define_attr "data" "int16,uint16,high_16,not_uint16" (const_string "int16"))
+
+(define_asm_attributes
+ [(set_attr "type" "multi")])
+
+;
+; C4x DELAY SLOTS
+;
+; Define delay slot scheduling for branch and call instructions.
+; The C[34]x has three delay slots. Note that none of the three instructions
+; that follow a delayed branch can be a Bcond, BcondD, BR, BRD, DBcond,
+; DBcondD, CALL, CALLcond, TRAPcond, RETIcond, RETScond, RPTB, RPTS, or IDLE.
+;
+; Annulled branches are a bit difficult because the next instructions
+; are preprocessed.
+; The table below shows what phase of the c4x is executed.
+; BccA[TF] label
+; op1 fetch, decode and read executed
+; op2 fetch and decode executed
+; op3 fetch executed
+; This means that we can allow any instruction in the last delay slot
+; and only instructions which modify registers in the first two.
+; lda can not be executed in the first delay slot
+; and ldpk can not be executed in the first two delay slots.
+
+(define_attr "onlyreg" "false,true"
+ (cond [(eq_attr "type" "unary,unarycc")
+ (if_then_else (and (match_operand 0 "reg_imm_operand" "")
+ (match_operand 1 "reg_imm_operand" ""))
+ (const_string "true") (const_string "false"))
+ (eq_attr "type" "binary,binarycc")
+ (if_then_else (and (match_operand 0 "reg_imm_operand" "")
+ (and (match_operand 1 "reg_imm_operand" "")
+ (match_operand 2 "reg_imm_operand" "")))
+ (const_string "true") (const_string "false"))]
+ (const_string "false")))
+
+(define_attr "onlyreg_nomod" "false,true"
+ (cond [(eq_attr "type" "unary,unarycc,compare,lda,store")
+ (if_then_else (and (match_operand 0 "not_modify_reg" "")
+ (match_operand 1 "not_modify_reg" ""))
+ (const_string "true") (const_string "false"))
+ (eq_attr "type" "binary,binarycc")
+ (if_then_else (and (match_operand 0 "not_modify_reg" "")
+ (and (match_operand 1 "not_modify_reg" "")
+ (match_operand 2 "not_modify_reg" "")))
+ (const_string "true") (const_string "false"))]
+ (const_string "false")))
+
+(define_attr "not_repeat_reg" "false,true"
+ (cond [(eq_attr "type" "unary,unarycc,compare,lda,ldp,store")
+ (if_then_else (and (match_operand 0 "not_rc_reg" "")
+ (match_operand 1 "not_rc_reg" ""))
+ (const_string "true") (const_string "false"))
+ (eq_attr "type" "binary,binarycc")
+ (if_then_else (and (match_operand 0 "not_rc_reg" "")
+ (and (match_operand 1 "not_rc_reg" "")
+ (match_operand 2 "not_rc_reg" "")))
+ (const_string "true") (const_string "false"))]
+ (const_string "false")))
+
+/* Disable compare because the c4x contains a bug. The cmpi insn sets the CC
+ in the read phase of the pipeline instead of the execution phase when
+ two registers are compared. */
+(define_attr "in_annul_slot_1" "false,true"
+ (if_then_else (and (and (eq_attr "cpu" "c4x")
+ (eq_attr "type" "!jump,call,rets,jmpc,compare,db,dbc,repeat,repeat_top,laj,push,pop,lda,ldp,multi"))
+ (eq_attr "onlyreg" "true"))
+ (const_string "true")
+ (const_string "false")))
+
+(define_attr "in_annul_slot_2" "false,true"
+ (if_then_else (and (and (eq_attr "cpu" "c4x")
+ (eq_attr "type" "!jump,call,rets,jmpc,db,dbc,repeat,repeat_top,laj,push,pop,ldp,multi"))
+ (eq_attr "onlyreg_nomod" "true"))
+ (const_string "true")
+ (const_string "false")))
+
+(define_attr "in_annul_slot_3" "false,true"
+ (if_then_else (and (eq_attr "cpu" "c4x")
+ (eq_attr "type" "!jump,call,rets,jmpc,db,dbc,repeat,repeat_top,laj,push,pop,multi"))
+ (const_string "true")
+ (const_string "false")))
+
+(define_attr "in_delay_slot" "false,true"
+ (if_then_else (eq_attr "type" "!jump,call,rets,jmpc,db,dbc,repeat,repeat_top,laj,multi")
+ (const_string "true")
+ (const_string "false")))
+
+(define_attr "in_repeat_slot" "false,true"
+ (if_then_else (and (eq_attr "cpu" "c4x")
+ (and (eq_attr "type" "!jump,call,rets,jmpc,db,dbc,repeat,repeat_top,laj,multi")
+ (eq_attr "not_repeat_reg" "true")))
+ (const_string "true")
+ (const_string "false")))
+
+(define_attr "in_dbc_slot" "false,true"
+ (if_then_else (eq_attr "type" "!jump,call,rets,jmpc,unarycc,binarycc,compare,db,dbc,repeat,repeat_top,laj,multi")
+ (const_string "true")
+ (const_string "false")))
+
+(define_delay (eq_attr "type" "jmpc")
+ [(eq_attr "in_delay_slot" "true")
+ (eq_attr "in_annul_slot_1" "true")
+ (eq_attr "in_annul_slot_1" "true")
+
+ (eq_attr "in_delay_slot" "true")
+ (eq_attr "in_annul_slot_2" "true")
+ (eq_attr "in_annul_slot_2" "true")
+
+ (eq_attr "in_delay_slot" "true")
+ (eq_attr "in_annul_slot_3" "true")
+ (eq_attr "in_annul_slot_3" "true") ])
+
+
+(define_delay (eq_attr "type" "repeat_top")
+ [(eq_attr "in_repeat_slot" "true") (nil) (nil)
+ (eq_attr "in_repeat_slot" "true") (nil) (nil)
+ (eq_attr "in_repeat_slot" "true") (nil) (nil)])
+
+(define_delay (eq_attr "type" "jump,db")
+ [(eq_attr "in_delay_slot" "true") (nil) (nil)
+ (eq_attr "in_delay_slot" "true") (nil) (nil)
+ (eq_attr "in_delay_slot" "true") (nil) (nil)])
+
+
+; Decrement and branch conditional instructions cannot modify the
+; condition codes for the cycles in the delay slots.
+;
+(define_delay (eq_attr "type" "dbc")
+ [(eq_attr "in_dbc_slot" "true") (nil) (nil)
+ (eq_attr "in_dbc_slot" "true") (nil) (nil)
+ (eq_attr "in_dbc_slot" "true") (nil) (nil)])
+
+; The LAJ instruction has three delay slots but the last slot is
+; used for pushing the return address. Thus we can only use two slots.
+;
+(define_delay (eq_attr "type" "laj")
+ [(eq_attr "in_delay_slot" "true") (nil) (nil)
+ (eq_attr "in_delay_slot" "true") (nil) (nil)])
+
+;
+; C4x UNSPEC NUMBERS
+;
+; 1 BU/BUD
+; 2 RPTS
+; 3 LSH
+; 4 cmphi
+; 5 RCPF
+; 6 RND
+; 7 repeat block filler
+; 8 loadhf_int
+; 9 storehf_int
+; 10 RSQRF
+; 11 loadqf_int
+; 12 storeqf_int
+; 22 rptb_init
+
+;
+; C4x FUNCTIONAL UNITS
+;
+; Define functional units for instruction scheduling to minimise
+; pipeline conflicts.
+;
+; With the C3x, an external memory write (with no wait states) takes
+; two cycles and an external memory read (with no wait states) takes
+; one cycle. However, an external read following an external write
+; takes two cycles. With internal memory, reads and writes take
+; half a cycle.
+;
+; When a C4x address register is loaded it will not be available for
+; an extra machine cycle. Calculating with a C4x address register
+; makes it unavailable for 2 machine cycles. To notify GCC of these
+; pipeline delays, each of the auxiliary and index registers are declared
+; as separate functional units.
+;
+; (define_function_unit NAME MULTIPLICITY SIMULTANEITY
+; TEST READY-DELAY ISSUE-DELAY [CONFLICT-LIST])
+;
+; MULTIPLICITY 1 (C4x has no independent identical function units)
+; SIMULTANEITY 0 (C4x is pipelined)
+; READY_DELAY 1 (Results usually ready after every cyle)
+; ISSUE_DELAY 1 (Can issue insns every cycle)
+
+; Just some dummy definitions. The real work is done in c4x_adjust_cost.
+; These are needed so the min/max READY_DELAY is known.
+
+(define_function_unit "dummy" 1 0 (const_int 0) 1 1)
+(define_function_unit "dummy" 1 0 (const_int 0) 2 1)
+(define_function_unit "dummy" 1 0 (const_int 0) 3 1)
+
+;(define_function_unit "ar0" 1 0
+; (and (eq_attr "cpu" "c4x")
+; (and (eq_attr "setar0" "1")
+; (eq_attr "usear0" "1")))
+; 3 1 )
+
+;(define_function_unit "ar0" 1 0
+; (and (eq_attr "cpu" "c4x")
+; (and (eq_attr "setlda_ar0" "1")
+; (eq_attr "usear0" "1")))
+; 2 1 )
+
+;(define_function_unit "ar0" 1 0
+; (and (eq_attr "cpu" "c4x")
+; (and (eq_attr "usear0" "1")
+; (eq_attr "readar0" "1")))
+; 2 1 )
+
+; The attribute setar0 is set to 1 for insns where ar0 is a dst operand.
+; Note that the attributes unarycc and binarycc do not apply
+; if ar0 is a dst operand (only loading an ext. prec. reg. sets CC)
+(define_attr "setar0" ""
+ (cond [(eq_attr "type" "unary,binary")
+ (if_then_else (match_operand 0 "ar0_reg_operand" "")
+ (const_int 1) (const_int 0))]
+ (const_int 0)))
+
+(define_attr "setlda_ar0" ""
+ (cond [(eq_attr "type" "lda")
+ (if_then_else (match_operand 0 "ar0_reg_operand" "")
+ (const_int 1) (const_int 0))]
+ (const_int 0)))
+
+; The attribute usear0 is set to 1 for insns where ar0 is used
+; for addressing, as a src operand, or as a dst operand.
+(define_attr "usear0" ""
+ (cond [(eq_attr "type" "compare,store")
+ (if_then_else (match_operand 0 "ar0_mem_operand" "")
+ (const_int 1) (const_int 0))
+ (eq_attr "type" "compare,lda,unary,unarycc,binary,binarycc")
+ (if_then_else (match_operand 1 "ar0_mem_operand" "")
+ (const_int 1) (const_int 0))
+ (eq_attr "type" "binary,binarycc")
+ (if_then_else (match_operand 2 "ar0_mem_operand" "")
+ (const_int 1) (const_int 0))
+ (eq_attr "type" "db,dbc")
+ (if_then_else (match_operand 0 "ar0_reg_operand" "")
+ (const_int 1) (const_int 0))]
+ (const_int 0)))
+
+; The attribute readar0 is set to 1 for insns where ar0 is a src operand.
+(define_attr "readar0" ""
+ (cond [(eq_attr "type" "compare")
+ (if_then_else (match_operand 0 "ar0_reg_operand" "")
+ (const_int 1) (const_int 0))
+ (eq_attr "type" "compare,store,lda,unary,unarycc,binary,binarycc")
+ (if_then_else (match_operand 1 "ar0_reg_operand" "")
+ (const_int 1) (const_int 0))
+ (eq_attr "type" "binary,binarycc")
+ (if_then_else (match_operand 2 "ar0_reg_operand" "")
+ (const_int 1) (const_int 0))]
+ (const_int 0)))
+
+;(define_function_unit "ar1" 1 0
+; (and (eq_attr "cpu" "c4x")
+; (and (eq_attr "setar1" "1")
+; (eq_attr "usear1" "1")))
+; 3 1 )
+
+;(define_function_unit "ar1" 1 0
+; (and (eq_attr "cpu" "c4x")
+; (and (eq_attr "setlda_ar1" "1")
+; (eq_attr "usear1" "1")))
+; 2 1 )
+
+;(define_function_unit "ar1" 1 0
+; (and (eq_attr "cpu" "c4x")
+; (and (eq_attr "usear1" "1")
+; (eq_attr "readar1" "1")))
+; 2 1 )
+
+(define_attr "setar1" ""
+ (cond [(eq_attr "type" "unary,binary")
+ (if_then_else (match_operand 0 "ar1_reg_operand" "")
+ (const_int 1) (const_int 0))]
+ (const_int 0)))
+
+(define_attr "setlda_ar1" ""
+ (cond [(eq_attr "type" "lda")
+ (if_then_else (match_operand 0 "ar1_reg_operand" "")
+ (const_int 1) (const_int 0))]
+ (const_int 0)))
+
+(define_attr "usear1" ""
+ (cond [(eq_attr "type" "compare,store")
+ (if_then_else (match_operand 0 "ar1_mem_operand" "")
+ (const_int 1) (const_int 0))
+ (eq_attr "type" "compare,lda,unary,unarycc,binary,binarycc")
+ (if_then_else (match_operand 1 "ar1_mem_operand" "")
+ (const_int 1) (const_int 0))
+ (eq_attr "type" "binary,binarycc")
+ (if_then_else (match_operand 2 "ar1_mem_operand" "")
+ (const_int 1) (const_int 0))
+ (eq_attr "type" "db,dbc")
+ (if_then_else (match_operand 0 "ar1_reg_operand" "")
+ (const_int 1) (const_int 0))]
+ (const_int 0)))
+
+(define_attr "readar1" ""
+ (cond [(eq_attr "type" "compare")
+ (if_then_else (match_operand 0 "ar1_reg_operand" "")
+ (const_int 1) (const_int 0))
+ (eq_attr "type" "compare,store,lda,unary,unarycc,binary,binarycc")
+ (if_then_else (match_operand 1 "ar1_reg_operand" "")
+ (const_int 1) (const_int 0))
+ (eq_attr "type" "binary,binarycc")
+ (if_then_else (match_operand 2 "ar1_reg_operand" "")
+ (const_int 1) (const_int 0))]
+ (const_int 0)))
+
+;(define_function_unit "ar2" 1 0
+; (and (eq_attr "cpu" "c4x")
+; (and (eq_attr "setar2" "1")
+; (eq_attr "usear2" "1")))
+; 3 1 )
+
+;(define_function_unit "ar2" 1 0
+; (and (eq_attr "cpu" "c4x")
+; (and (eq_attr "setlda_ar2" "1")
+; (eq_attr "usear2" "1")))
+; 2 1 )
+
+;(define_function_unit "ar2" 1 0
+; (and (eq_attr "cpu" "c4x")
+; (and (eq_attr "usear2" "1")
+; (eq_attr "readar2" "1")))
+; 2 1 )
+
+(define_attr "setar2" ""
+ (cond [(eq_attr "type" "unary,binary")
+ (if_then_else (match_operand 0 "ar2_reg_operand" "")
+ (const_int 1) (const_int 0))]
+ (const_int 0)))
+
+(define_attr "setlda_ar2" ""
+ (cond [(eq_attr "type" "lda")
+ (if_then_else (match_operand 0 "ar2_reg_operand" "")
+ (const_int 1) (const_int 0))]
+ (const_int 0)))
+
+(define_attr "usear2" ""
+ (cond [(eq_attr "type" "compare,store")
+ (if_then_else (match_operand 0 "ar2_mem_operand" "")
+ (const_int 1) (const_int 0))
+ (eq_attr "type" "compare,lda,unary,unarycc,binary,binarycc")
+ (if_then_else (match_operand 1 "ar2_mem_operand" "")
+ (const_int 1) (const_int 0))
+ (eq_attr "type" "binary,binarycc")
+ (if_then_else (match_operand 2 "ar2_mem_operand" "")
+ (const_int 1) (const_int 0))
+ (eq_attr "type" "db,dbc")
+ (if_then_else (match_operand 0 "ar2_reg_operand" "")
+ (const_int 1) (const_int 0))]
+ (const_int 0)))
+
+(define_attr "readar2" ""
+ (cond [(eq_attr "type" "compare")
+ (if_then_else (match_operand 0 "ar2_reg_operand" "")
+ (const_int 1) (const_int 0))
+ (eq_attr "type" "compare,store,lda,unary,unarycc,binary,binarycc")
+ (if_then_else (match_operand 1 "ar2_reg_operand" "")
+ (const_int 1) (const_int 0))
+ (eq_attr "type" "binary,binarycc")
+ (if_then_else (match_operand 2 "ar2_reg_operand" "")
+ (const_int 1) (const_int 0))]
+ (const_int 0)))
+
+;(define_function_unit "ar3" 1 0
+; (and (eq_attr "cpu" "c4x")
+; (and (eq_attr "setar3" "1")
+; (eq_attr "usear3" "1")))
+; 3 1 )
+
+;(define_function_unit "ar3" 1 0
+; (and (eq_attr "cpu" "c4x")
+; (and (eq_attr "setlda_ar3" "1")
+; (eq_attr "usear3" "1")))
+; 2 1 )
+
+;(define_function_unit "ar3" 1 0
+; (and (eq_attr "cpu" "c4x")
+; (and (eq_attr "usear3" "1")
+; (eq_attr "readar3" "1")))
+; 2 1 )
+
+(define_attr "setar3" ""
+ (cond [(eq_attr "type" "unary,binary")
+ (if_then_else (match_operand 0 "ar3_reg_operand" "")
+ (const_int 1) (const_int 0))]
+ (const_int 0)))
+
+(define_attr "setlda_ar3" ""
+ (cond [(eq_attr "type" "lda")
+ (if_then_else (match_operand 0 "ar3_reg_operand" "")
+ (const_int 1) (const_int 0))]
+ (const_int 0)))
+
+(define_attr "usear3" ""
+ (cond [(eq_attr "type" "compare,store")
+ (if_then_else (match_operand 0 "ar3_mem_operand" "")
+ (const_int 1) (const_int 0))
+ (eq_attr "type" "compare,lda,unary,unarycc,binary,binarycc")
+ (if_then_else (match_operand 1 "ar3_mem_operand" "")
+ (const_int 1) (const_int 0))
+ (eq_attr "type" "binary,binarycc")
+ (if_then_else (match_operand 2 "ar3_mem_operand" "")
+ (const_int 1) (const_int 0))
+ (eq_attr "type" "db,dbc")
+ (if_then_else (match_operand 0 "ar3_reg_operand" "")
+ (const_int 1) (const_int 0))]
+ (const_int 0)))
+
+(define_attr "readar3" ""
+ (cond [(eq_attr "type" "compare")
+ (if_then_else (match_operand 0 "ar3_reg_operand" "")
+ (const_int 1) (const_int 0))
+ (eq_attr "type" "compare,store,lda,unary,unarycc,binary,binarycc")
+ (if_then_else (match_operand 1 "ar3_reg_operand" "")
+ (const_int 1) (const_int 0))
+ (eq_attr "type" "binary,binarycc")
+ (if_then_else (match_operand 2 "ar3_reg_operand" "")
+ (const_int 1) (const_int 0))]
+ (const_int 0)))
+
+;(define_function_unit "ar4" 1 0
+; (and (eq_attr "cpu" "c4x")
+; (and (eq_attr "setar4" "1")
+; (eq_attr "usear4" "1")))
+; 3 1 )
+
+;(define_function_unit "ar4" 1 0
+; (and (eq_attr "cpu" "c4x")
+; (and (eq_attr "setlda_ar4" "1")
+; (eq_attr "usear4" "1")))
+; 2 1 )
+
+;(define_function_unit "ar4" 1 0
+; (and (eq_attr "cpu" "c4x")
+; (and (eq_attr "usear4" "1")
+; (eq_attr "readar4" "1")))
+; 2 1 )
+
+(define_attr "setar4" ""
+ (cond [(eq_attr "type" "unary,binary")
+ (if_then_else (match_operand 0 "ar4_reg_operand" "")
+ (const_int 1) (const_int 0))]
+ (const_int 0)))
+
+(define_attr "setlda_ar4" ""
+ (cond [(eq_attr "type" "lda")
+ (if_then_else (match_operand 0 "ar4_reg_operand" "")
+ (const_int 1) (const_int 0))]
+ (const_int 0)))
+
+(define_attr "usear4" ""
+ (cond [(eq_attr "type" "compare,store")
+ (if_then_else (match_operand 0 "ar4_mem_operand" "")
+ (const_int 1) (const_int 0))
+ (eq_attr "type" "compare,lda,unary,unarycc,binary,binarycc")
+ (if_then_else (match_operand 1 "ar4_mem_operand" "")
+ (const_int 1) (const_int 0))
+ (eq_attr "type" "binary,binarycc")
+ (if_then_else (match_operand 2 "ar4_mem_operand" "")
+ (const_int 1) (const_int 0))
+ (eq_attr "type" "db,dbc")
+ (if_then_else (match_operand 0 "ar4_reg_operand" "")
+ (const_int 1) (const_int 0))]
+ (const_int 0)))
+
+(define_attr "readar4" ""
+ (cond [(eq_attr "type" "compare")
+ (if_then_else (match_operand 0 "ar4_reg_operand" "")
+ (const_int 1) (const_int 0))
+ (eq_attr "type" "compare,store,lda,unary,unarycc,binary,binarycc")
+ (if_then_else (match_operand 1 "ar4_reg_operand" "")
+ (const_int 1) (const_int 0))
+ (eq_attr "type" "binary,binarycc")
+ (if_then_else (match_operand 2 "ar4_reg_operand" "")
+ (const_int 1) (const_int 0))]
+ (const_int 0)))
+
+;(define_function_unit "ar5" 1 0
+; (and (eq_attr "cpu" "c4x")
+; (and (eq_attr "setar5" "1")
+; (eq_attr "usear5" "1")))
+; 3 1 )
+
+;(define_function_unit "ar5" 1 0
+; (and (eq_attr "cpu" "c4x")
+; (and (eq_attr "setlda_ar5" "1")
+; (eq_attr "usear5" "1")))
+; 2 1 )
+
+;(define_function_unit "ar5" 1 0
+; (and (eq_attr "cpu" "c4x")
+; (and (eq_attr "usear5" "1")
+; (eq_attr "readar5" "1")))
+; 2 1 )
+
+(define_attr "setar5" ""
+ (cond [(eq_attr "type" "unary,binary")
+ (if_then_else (match_operand 0 "ar5_reg_operand" "")
+ (const_int 1) (const_int 0))]
+ (const_int 0)))
+
+(define_attr "setlda_ar5" ""
+ (cond [(eq_attr "type" "lda")
+ (if_then_else (match_operand 0 "ar5_reg_operand" "")
+ (const_int 1) (const_int 0))]
+ (const_int 0)))
+
+(define_attr "usear5" ""
+ (cond [(eq_attr "type" "compare,store")
+ (if_then_else (match_operand 0 "ar5_mem_operand" "")
+ (const_int 1) (const_int 0))
+ (eq_attr "type" "compare,lda,unary,unarycc,binary,binarycc")
+ (if_then_else (match_operand 1 "ar5_mem_operand" "")
+ (const_int 1) (const_int 0))
+ (eq_attr "type" "binary,binarycc")
+ (if_then_else (match_operand 2 "ar5_mem_operand" "")
+ (const_int 1) (const_int 0))
+ (eq_attr "type" "db,dbc")
+ (if_then_else (match_operand 0 "ar5_reg_operand" "")
+ (const_int 1) (const_int 0))]
+ (const_int 0)))
+
+(define_attr "readar5" ""
+ (cond [(eq_attr "type" "compare")
+ (if_then_else (match_operand 0 "ar5_reg_operand" "")
+ (const_int 1) (const_int 0))
+ (eq_attr "type" "compare,store,lda,unary,unarycc,binary,binarycc")
+ (if_then_else (match_operand 1 "ar5_reg_operand" "")
+ (const_int 1) (const_int 0))
+ (eq_attr "type" "binary,binarycc")
+ (if_then_else (match_operand 2 "ar5_reg_operand" "")
+ (const_int 1) (const_int 0))]
+ (const_int 0)))
+
+;(define_function_unit "ar6" 1 0
+; (and (eq_attr "cpu" "c4x")
+; (and (eq_attr "setar6" "1")
+; (eq_attr "usear6" "1")))
+; 3 1 )
+
+;(define_function_unit "ar6" 1 0
+; (and (eq_attr "cpu" "c4x")
+; (and (eq_attr "setlda_ar6" "1")
+; (eq_attr "usear6" "1")))
+; 2 1 )
+
+;(define_function_unit "ar6" 1 0
+; (and (eq_attr "cpu" "c4x")
+; (and (eq_attr "usear6" "1")
+; (eq_attr "readar6" "1")))
+; 2 1 )
+
+(define_attr "setar6" ""
+ (cond [(eq_attr "type" "unary,binary")
+ (if_then_else (match_operand 0 "ar6_reg_operand" "")
+ (const_int 1) (const_int 0))]
+ (const_int 0)))
+
+(define_attr "setlda_ar6" ""
+ (cond [(eq_attr "type" "lda")
+ (if_then_else (match_operand 0 "ar6_reg_operand" "")
+ (const_int 1) (const_int 0))]
+ (const_int 0)))
+
+(define_attr "usear6" ""
+ (cond [(eq_attr "type" "compare,store")
+ (if_then_else (match_operand 0 "ar6_mem_operand" "")
+ (const_int 1) (const_int 0))
+ (eq_attr "type" "compare,lda,unary,unarycc,binary,binarycc")
+ (if_then_else (match_operand 1 "ar6_mem_operand" "")
+ (const_int 1) (const_int 0))
+ (eq_attr "type" "binary,binarycc")
+ (if_then_else (match_operand 2 "ar6_mem_operand" "")
+ (const_int 1) (const_int 0))
+ (eq_attr "type" "db,dbc")
+ (if_then_else (match_operand 0 "ar6_reg_operand" "")
+ (const_int 1) (const_int 0))]
+ (const_int 0)))
+
+(define_attr "readar6" ""
+ (cond [(eq_attr "type" "compare")
+ (if_then_else (match_operand 0 "ar6_reg_operand" "")
+ (const_int 1) (const_int 0))
+ (eq_attr "type" "compare,store,lda,unary,unarycc,binary,binarycc")
+ (if_then_else (match_operand 1 "ar6_reg_operand" "")
+ (const_int 1) (const_int 0))
+ (eq_attr "type" "binary,binarycc")
+ (if_then_else (match_operand 2 "ar6_reg_operand" "")
+ (const_int 1) (const_int 0))]
+ (const_int 0)))
+
+;(define_function_unit "ar7" 1 0
+; (and (eq_attr "cpu" "c4x")
+; (and (eq_attr "setar7" "1")
+; (eq_attr "usear7" "1")))
+; 3 1 )
+
+;(define_function_unit "ar7" 1 0
+; (and (eq_attr "cpu" "c4x")
+; (and (eq_attr "setlda_ar7" "1")
+; (eq_attr "usear7" "1")))
+; 2 1 )
+
+;(define_function_unit "ar7" 1 0
+; (and (eq_attr "cpu" "c4x")
+; (and (eq_attr "usear7" "1")
+; (eq_attr "readar7" "1")))
+; 2 1 )
+
+(define_attr "setar7" ""
+ (cond [(eq_attr "type" "unary,binary")
+ (if_then_else (match_operand 0 "ar7_reg_operand" "")
+ (const_int 1) (const_int 0))]
+ (const_int 0)))
+
+(define_attr "setlda_ar7" ""
+ (cond [(eq_attr "type" "lda")
+ (if_then_else (match_operand 0 "ar7_reg_operand" "")
+ (const_int 1) (const_int 0))]
+ (const_int 0)))
+
+(define_attr "usear7" ""
+ (cond [(eq_attr "type" "compare,store")
+ (if_then_else (match_operand 0 "ar7_mem_operand" "")
+ (const_int 1) (const_int 0))
+ (eq_attr "type" "compare,lda,unary,unarycc,binary,binarycc")
+ (if_then_else (match_operand 1 "ar7_mem_operand" "")
+ (const_int 1) (const_int 0))
+ (eq_attr "type" "binary,binarycc")
+ (if_then_else (match_operand 2 "ar7_mem_operand" "")
+ (const_int 1) (const_int 0))
+ (eq_attr "type" "db,dbc")
+ (if_then_else (match_operand 0 "ar7_reg_operand" "")
+ (const_int 1) (const_int 0))]
+ (const_int 0)))
+
+(define_attr "readar7" ""
+ (cond [(eq_attr "type" "compare")
+ (if_then_else (match_operand 0 "ar7_reg_operand" "")
+ (const_int 1) (const_int 0))
+ (eq_attr "type" "compare,store,lda,unary,unarycc,binary,binarycc")
+ (if_then_else (match_operand 1 "ar7_reg_operand" "")
+ (const_int 1) (const_int 0))
+ (eq_attr "type" "binary,binarycc")
+ (if_then_else (match_operand 2 "ar7_reg_operand" "")
+ (const_int 1) (const_int 0))]
+ (const_int 0)))
+
+;(define_function_unit "ir0" 1 0
+; (and (eq_attr "cpu" "c4x")
+; (and (eq_attr "setir0" "1")
+; (eq_attr "useir0" "1")))
+; 3 1 )
+
+;(define_function_unit "ir0" 1 0
+; (and (eq_attr "cpu" "c4x")
+; (and (eq_attr "setlda_ir0" "1")
+; (eq_attr "useir0" "1")))
+; 2 1 )
+
+(define_attr "setir0" ""
+ (cond [(eq_attr "type" "unary,binary")
+ (if_then_else (match_operand 0 "ir0_reg_operand" "")
+ (const_int 1) (const_int 0))]
+ (const_int 0)))
+
+(define_attr "setlda_ir0" ""
+ (cond [(eq_attr "type" "lda")
+ (if_then_else (match_operand 0 "ir0_reg_operand" "")
+ (const_int 1) (const_int 0))]
+ (const_int 0)))
+
+(define_attr "useir0" ""
+ (cond [(eq_attr "type" "compare,store")
+ (if_then_else (match_operand 0 "ir0_mem_operand" "")
+ (const_int 1) (const_int 0))
+ (eq_attr "type" "compare,lda,unary,unarycc,binary,binarycc")
+ (if_then_else (match_operand 1 "ir0_mem_operand" "")
+ (const_int 1) (const_int 0))
+ (eq_attr "type" "binary,binarycc")
+ (if_then_else (match_operand 2 "ir0_mem_operand" "")
+ (const_int 1) (const_int 0))]
+ (const_int 0)))
+
+;(define_function_unit "ir1" 1 0
+; (and (eq_attr "cpu" "c4x")
+; (and (eq_attr "setir1" "1")
+; (eq_attr "useir1" "1")))
+; 3 1 )
+
+;(define_function_unit "ir1" 1 0
+; (and (eq_attr "cpu" "c4x")
+; (and (eq_attr "setlda_ir1" "1")
+; (eq_attr "useir1" "1")))
+; 2 1 )
+
+(define_attr "setir1" ""
+ (cond [(eq_attr "type" "unary,binary")
+ (if_then_else (match_operand 0 "ir1_reg_operand" "")
+ (const_int 1) (const_int 0))]
+ (const_int 0)))
+
+(define_attr "setlda_ir1" ""
+ (cond [(eq_attr "type" "lda")
+ (if_then_else (match_operand 0 "ir1_reg_operand" "")
+ (const_int 1) (const_int 0))]
+ (const_int 0)))
+
+(define_attr "useir1" ""
+ (cond [(eq_attr "type" "compare,store")
+ (if_then_else (match_operand 0 "ir1_mem_operand" "")
+ (const_int 1) (const_int 0))
+ (eq_attr "type" "compare,lda,unary,unarycc,binary,binarycc")
+ (if_then_else (match_operand 1 "ir1_mem_operand" "")
+ (const_int 1) (const_int 0))
+ (eq_attr "type" "binary,binarycc")
+ (if_then_else (match_operand 2 "ir1_mem_operand" "")
+ (const_int 1) (const_int 0))]
+ (const_int 0)))
+
+; With the C3x, things are simpler, but slower, i.e. more pipeline conflicts :(
+; There are three functional groups:
+; (1) AR0-AR7, IR0-IR1, BK
+; (2) DP
+; (3) SP
+;
+; When a register in one of these functional groups is loaded,
+; the contents of that or any other register in its group
+; will not be available to the next instruction for 2 machine cycles.
+; Similarly, when a register in one of the functional groups is read
+; excepting (IR0-IR1, BK, DP) the contents of that or any other register
+; in its group will not be available to the next instruction for
+; 1 machine cycle.
+;
+; Let's ignore functional groups 2 and 3 for now, since they are not
+; so important.
+
+;(define_function_unit "group1" 1 0
+; (and (eq_attr "cpu" "c3x")
+; (and (eq_attr "setgroup1" "1")
+; (eq_attr "usegroup1" "1")))
+; 3 1)
+
+;(define_function_unit "group1" 1 0
+; (and (eq_attr "cpu" "c3x")
+; (and (eq_attr "usegroup1" "1")
+; (eq_attr "readarx" "1")))
+; 2 1)
+
+(define_attr "setgroup1" ""
+ (cond [(eq_attr "type" "lda,unary,binary")
+ (if_then_else (match_operand 0 "group1_reg_operand" "")
+ (const_int 1) (const_int 0))]
+ (const_int 0)))
+
+(define_attr "usegroup1" ""
+ (cond [(eq_attr "type" "compare,store,store_store,store_load")
+ (if_then_else (match_operand 0 "group1_mem_operand" "")
+ (const_int 1) (const_int 0))
+ (eq_attr "type" "compare,lda,unary,unarycc,binary,binarycc,load_load,load_store")
+ (if_then_else (match_operand 1 "group1_mem_operand" "")
+ (const_int 1) (const_int 0))
+ (eq_attr "type" "store_store,load_store")
+ (if_then_else (match_operand 2 "group1_mem_operand" "")
+ (const_int 1) (const_int 0))
+ (eq_attr "type" "load_load,store_load")
+ (if_then_else (match_operand 3 "group1_mem_operand" "")
+ (const_int 1) (const_int 0))]
+ (const_int 0)))
+
+(define_attr "readarx" ""
+ (cond [(eq_attr "type" "compare")
+ (if_then_else (match_operand 0 "arx_reg_operand" "")
+ (const_int 1) (const_int 0))
+ (eq_attr "type" "compare,store,lda,unary,unarycc,binary,binarycc")
+ (if_then_else (match_operand 1 "arx_reg_operand" "")
+ (const_int 1) (const_int 0))
+ (eq_attr "type" "binary,binarycc")
+ (if_then_else (match_operand 2 "arx_reg_operand" "")
+ (const_int 1) (const_int 0))]
+ (const_int 0)))
+
+
+;
+; C4x INSN PATTERNS:
+;
+; Note that the movMM and addP patterns can be called during reload
+; so we need to take special care with theses patterns since
+; we cannot blindly clobber CC or generate new pseudo registers.
+
+;
+; TWO OPERAND INTEGER INSTRUCTIONS
+;
+
+;
+; LDP/LDPK
+;
+(define_insn "set_ldp"
+ [(set (match_operand:QI 0 "dp_reg_operand" "=z")
+ (high:QI (match_operand:QI 1 "" "")))]
+ "! TARGET_SMALL"
+ "* return (TARGET_C3X) ? \"ldp\\t%A1\" : \"ldpk\\t%A1\";"
+ [(set_attr "type" "ldp")])
+
+(define_insn "set_high"
+ [(set (match_operand:QI 0 "std_reg_operand" "=c")
+ (high:QI (match_operand:QI 1 "symbolic_address_operand" "")))]
+ "! TARGET_C3X "
+ "ldhi\\t^%H1,%0"
+ [(set_attr "type" "unary")])
+
+(define_insn "set_lo_sum"
+ [(set (match_operand:QI 0 "std_reg_operand" "=c")
+ (lo_sum:QI (match_dup 0)
+ (match_operand:QI 1 "symbolic_address_operand" "")))]
+ ""
+ "or\\t#%H1,%0"
+ [(set_attr "type" "unary")])
+
+(define_split
+ [(set (match_operand:QI 0 "std_reg_operand" "")
+ (match_operand:QI 1 "symbolic_address_operand" ""))]
+ "! TARGET_C3X"
+ [(set (match_dup 0) (high:QI (match_dup 1)))
+ (set (match_dup 0) (lo_sum:QI (match_dup 0) (match_dup 1)))]
+ "")
+
+(define_split
+ [(set (match_operand:QI 0 "std_reg_operand" "")
+ (match_operand:QI 1 "const_int_operand" ""))]
+ "! TARGET_C3X
+ && (INTVAL (operands[1]) & ~0xffff) != 0
+ && (INTVAL (operands[1]) & 0xffff) != 0"
+ [(set (match_dup 0) (match_dup 2))
+ (set (match_dup 0) (ior:QI (match_dup 0) (match_dup 3)))]
+ "
+{
+ operands[2] = gen_rtx (CONST_INT, VOIDmode, INTVAL (operands[1]) & ~0xffff);
+ operands[3] = gen_rtx (CONST_INT, VOIDmode, INTVAL (operands[1]) & 0xffff);
+}")
+
+; This pattern is required to handle the case where a register that clobbers
+; CC has been selected to load a symbolic address. We force the address
+; into memory and then generate LDP and LDIU insns.
+; This is also required for the C30 if we pretend that we can
+; easily load symbolic addresses into a register.
+(define_split
+ [(set (match_operand:QI 0 "reg_operand" "")
+ (match_operand:QI 1 "symbolic_address_operand" ""))]
+ "! TARGET_SMALL
+ && (TARGET_C3X || (reload_completed
+ && ! std_reg_operand (operands[0], QImode)))"
+ [(set (match_dup 2) (high:QI (match_dup 3)))
+ (set (match_dup 0) (match_dup 4))
+ (use (match_dup 1))]
+ "
+{
+ rtx dp_reg = gen_rtx_REG (Pmode, DP_REGNO);
+ operands[2] = dp_reg;
+ operands[3] = force_const_mem (Pmode, operands[1]);
+ operands[4] = change_address (operands[3], QImode,
+ gen_rtx_LO_SUM (Pmode, dp_reg,
+ XEXP (operands[3], 0)));
+ operands[3] = XEXP (operands[3], 0);
+}")
+
+; This pattern is similar to the above but does not emit a LDP
+; for the small memory model.
+(define_split
+ [(set (match_operand:QI 0 "reg_operand" "")
+ (match_operand:QI 1 "symbolic_address_operand" ""))]
+ "TARGET_SMALL
+ && (TARGET_C3X || (reload_completed
+ && ! std_reg_operand (operands[0], QImode)))"
+ [(set (match_dup 0) (match_dup 2))
+ (use (match_dup 1))]
+ "
+{
+ rtx dp_reg = gen_rtx_REG (Pmode, DP_REGNO);
+ operands[2] = force_const_mem (Pmode, operands[1]);
+ operands[2] = change_address (operands[2], QImode,
+ gen_rtx_LO_SUM (Pmode, dp_reg,
+ XEXP (operands[2], 0)));
+}")
+
+(define_insn "load_immed_address"
+ [(set (match_operand:QI 0 "reg_operand" "=a?x?c*r")
+ (match_operand:QI 1 "symbolic_address_operand" ""))]
+ "TARGET_LOAD_ADDRESS"
+ "#"
+ [(set_attr "type" "multi")])
+
+;
+; LDIU/LDA/STI/STIK
+;
+; The following moves will not set the condition codes register.
+;
+
+; This must come before the general case
+(define_insn "*movqi_stik"
+ [(set (match_operand:QI 0 "memory_operand" "=m")
+ (match_operand:QI 1 "stik_const_operand" "K"))]
+ "! TARGET_C3X"
+ "stik\\t%1,%0"
+ [(set_attr "type" "store")])
+
+; We must provide an alternative to store to memory in case we have to
+; spill a register.
+(define_insn "movqi_noclobber"
+ [(set (match_operand:QI 0 "src_operand" "=d,*c,m,r")
+ (match_operand:QI 1 "src_hi_operand" "rIm,rIm,r,O"))]
+ "(REG_P (operands[0]) || REG_P (operands[1])
+ || GET_CODE (operands[0]) == SUBREG
+ || GET_CODE (operands[1]) == SUBREG)
+ && ! symbolic_address_operand (operands[1], QImode)"
+ "*
+ if (which_alternative == 2)
+ return \"sti\\t%1,%0\";
+
+ if (! TARGET_C3X && which_alternative == 3)
+ {
+ operands[1] = GEN_INT ((INTVAL (operands[1]) >> 16) & 0xffff);
+ return \"ldhi\\t%1,%0\";
+ }
+
+ /* The lda instruction cannot use the same register as source
+ and destination. */
+ if (! TARGET_C3X && which_alternative == 1
+ && ( IS_ADDR_REG (REGNO (operands[0]))
+ || IS_INDEX_REG (REGNO (operands[0]))
+ || IS_SP_REG (REGNO (operands[0])))
+ && (REGNO (operands[0]) != REGNO (operands[1])))
+ return \"lda\\t%1,%0\";
+ return \"ldiu\\t%1,%0\";
+ "
+ [(set_attr "type" "unary,lda,store,unary")
+ (set_attr "data" "int16,int16,int16,high_16")])
+
+;
+; LDI
+;
+
+; We shouldn't need these peepholes, but the combiner seems to miss them...
+(define_peephole
+ [(set (match_operand:QI 0 "ext_reg_operand" "=d")
+ (match_operand:QI 1 "src_operand" "rIm"))
+ (set (reg:CC 21)
+ (compare:CC (match_dup 0) (const_int 0)))]
+ ""
+ "@
+ ldi\\t%1,%0"
+ [(set_attr "type" "unarycc")
+ (set_attr "data" "int16")])
+
+(define_insn "*movqi_set"
+ [(set (reg:CC 21)
+ (compare:CC (match_operand:QI 1 "src_operand" "rIm")
+ (const_int 0)))
+ (set (match_operand:QI 0 "ext_reg_operand" "=d")
+ (match_dup 1))]
+ ""
+ "@
+ ldi\\t%1,%0"
+ [(set_attr "type" "unarycc")
+ (set_attr "data" "int16")])
+
+; This pattern probably gets in the way and requires a scratch register
+; when a simple compare with zero will suffice.
+;(define_insn "*movqi_test"
+; [(set (reg:CC 21)
+; (compare:CC (match_operand:QI 1 "src_operand" "rIm")
+; (const_int 0)))
+; (clobber (match_scratch:QI 0 "=d"))]
+; ""
+; "@
+; ldi\\t%1,%0"
+; [(set_attr "type" "unarycc")
+; (set_attr "data" "int16")])
+
+; If one of the operands is not a register, then we should
+; emit two insns, using a scratch register. This will produce
+; better code in loops if the source operand is invariant, since
+; the source reload can be optimised out. During reload we cannot
+; use change_address or force_reg which will allocate new pseudo regs.
+
+; Unlike most other insns, the move insns can't be split with
+; different predicates, because register spilling and other parts of
+; the compiler, have memoized the insn number already.
+
+(define_expand "movqi"
+ [(set (match_operand:QI 0 "general_operand" "")
+ (match_operand:QI 1 "general_operand" ""))]
+ ""
+ "
+{
+ if (c4x_emit_move_sequence (operands, QImode))
+ DONE;
+}")
+
+(define_insn "*movqi_update"
+ [(set (match_operand:QI 0 "reg_operand" "=r")
+ (mem:QI (plus:QI (match_operand:QI 1 "addr_reg_operand" "a")
+ (match_operand:QI 2 "index_reg_operand" "x"))))
+ (set (match_dup 1)
+ (plus:QI (match_dup 1) (match_dup 2)))]
+ ""
+ "ldiu\\t*%1++(%2),%0"
+ [(set_attr "type" "unary")
+ (set_attr "data" "int16")])
+
+(define_insn "movqi_parallel"
+ [(set (match_operand:QI 0 "parallel_operand" "=q,S<>,q,S<>")
+ (match_operand:QI 1 "parallel_operand" "S<>,q,S<>,q"))
+ (set (match_operand:QI 2 "parallel_operand" "=q,S<>,S<>,q")
+ (match_operand:QI 3 "parallel_operand" "S<>,q,q,S<>"))]
+ "valid_parallel_load_store (operands, QImode)"
+ "@
+ ldi1\\t%1,%0\\n||\\tldi2\\t%3,%2
+ sti1\\t%1,%0\\n||\\tsti2\\t%3,%2
+ ldi\\t%1,%0\\n||\\tsti\\t%3,%2
+ ldi\\t%3,%2\\n||\\tsti\\t%1,%0"
+ [(set_attr "type" "load_load,store_store,load_store,store_load")])
+
+;
+; PUSH/POP
+;
+(define_insn "*pushqi"
+ [(set (mem:QI (pre_inc:QI (reg:QI 20)))
+ (match_operand:QI 0 "reg_operand" "r"))]
+ ""
+ "push\\t%0"
+ [(set_attr "type" "push")])
+
+(define_insn "*popqi"
+ [(set (match_operand:QI 0 "reg_operand" "=r")
+ (mem:QI (post_dec:QI (reg:QI 20))))
+ (clobber (reg:CC 21))]
+ ""
+ "pop\\t%0"
+ [(set_attr "type" "pop")])
+
+;
+; ABSI
+;
+(define_expand "absqi2"
+ [(parallel [(set (match_operand:QI 0 "reg_operand" "")
+ (abs:QI (match_operand:QI 1 "src_operand" "")))
+ (clobber (reg:CC_NOOV 21))])]
+ ""
+ "")
+
+(define_insn "*absqi2_clobber"
+ [(set (match_operand:QI 0 "reg_operand" "=d,c")
+ (abs:QI (match_operand:QI 1 "src_operand" "rIm,rIm")))
+ (clobber (reg:CC_NOOV 21))]
+ ""
+ "absi\\t%1,%0"
+ [(set_attr "type" "unarycc,unary")
+ (set_attr "data" "int16,int16")])
+
+(define_insn "*absqi2_test"
+ [(set (reg:CC_NOOV 21)
+ (compare:CC_NOOV (abs:QI (match_operand:QI 1 "src_operand" "rIm"))
+ (const_int 0)))
+ (clobber (match_scratch:QI 0 "=d"))]
+ ""
+ "absi\\t%1,%0"
+ [(set_attr "type" "unarycc")
+ (set_attr "data" "int16")])
+
+(define_insn "*absqi2_set"
+ [(set (reg:CC_NOOV 21)
+ (compare:CC_NOOV (abs:QI (match_operand:QI 1 "src_operand" "rIm"))
+ (const_int 0)))
+ (set (match_operand:QI 0 "ext_reg_operand" "=d")
+ (abs:QI (match_dup 1)))]
+ ""
+ "absi\\t%1,%0"
+ [(set_attr "type" "unarycc")
+ (set_attr "data" "int16")])
+
+;
+; NEGI
+;
+(define_expand "negqi2"
+ [(parallel [(set (match_operand:QI 0 "reg_operand" "")
+ (neg:QI (match_operand:QI 1 "src_operand" "")))
+ (clobber (reg:CC_NOOV 21))])]
+""
+"")
+
+(define_insn "*negqi2_clobber"
+ [(set (match_operand:QI 0 "reg_operand" "=d,c")
+ (neg:QI (match_operand:QI 1 "src_operand" "rIm,rIm")))
+ (clobber (reg:CC_NOOV 21))]
+ ""
+ "negi\\t%1,%0"
+ [(set_attr "type" "unarycc,unary")
+ (set_attr "data" "int16,int16")])
+
+(define_insn "*negqi2_test"
+ [(set (reg:CC_NOOV 21)
+ (compare:CC_NOOV (neg:QI (match_operand:QI 1 "src_operand" "rIm"))
+ (const_int 0)))
+ (clobber (match_scratch:QI 0 "=d"))]
+ ""
+ "negi\\t%1,%0"
+ [(set_attr "type" "unarycc")
+ (set_attr "data" "int16")])
+
+(define_insn "*negqi2_set"
+ [(set (reg:CC_NOOV 21)
+ (compare:CC_NOOV (neg:QI (match_operand:QI 1 "src_operand" "rIm"))
+ (const_int 0)))
+ (set (match_operand:QI 0 "ext_reg_operand" "=d")
+ (neg:QI (match_dup 1)))]
+ ""
+ "negi\\t%1,%0"
+ [(set_attr "type" "unarycc")
+ (set_attr "data" "int16")])
+
+(define_insn "*negbqi2_clobber"
+ [(set (match_operand:QI 0 "ext_reg_operand" "=d")
+ (neg:QI (match_operand:QI 1 "src_operand" "rIm")))
+ (use (reg:CC_NOOV 21))
+ (clobber (reg:CC_NOOV 21))]
+ ""
+ "negb\\t%1,%0"
+ [(set_attr "type" "unarycc")
+ (set_attr "data" "int16")])
+
+;
+; NOT
+;
+(define_expand "one_cmplqi2"
+ [(parallel [(set (match_operand:QI 0 "reg_operand" "")
+ (not:QI (match_operand:QI 1 "lsrc_operand" "")))
+ (clobber (reg:CC 21))])]
+ ""
+ "")
+
+(define_insn "*one_cmplqi2_clobber"
+ [(set (match_operand:QI 0 "reg_operand" "=d,c")
+ (not:QI (match_operand:QI 1 "lsrc_operand" "rLm,rLm")))
+ (clobber (reg:CC 21))]
+ ""
+ "not\\t%1,%0"
+ [(set_attr "type" "unarycc,unary")
+ (set_attr "data" "uint16,uint16")])
+
+(define_insn "*one_cmplqi2_test"
+ [(set (reg:CC 21)
+ (compare:CC (not:QI (match_operand:QI 1 "lsrc_operand" "rLm"))
+ (const_int 0)))
+ (clobber (match_scratch:QI 0 "=d"))]
+ ""
+ "not\\t%1,%0"
+ [(set_attr "type" "unarycc")
+ (set_attr "data" "uint16")])
+
+(define_insn "*one_cmplqi2_set"
+ [(set (reg:CC 21)
+ (compare:CC (not:QI (match_operand:QI 1 "lsrc_operand" "rLm"))
+ (const_int 0)))
+ (set (match_operand:QI 0 "ext_reg_operand" "=d")
+ (not:QI (match_dup 1)))]
+ ""
+ "not\\t%1,%0"
+ [(set_attr "type" "unarycc")
+ (set_attr "data" "uint16")])
+
+(define_insn "*one_cmplqi2_const_clobber"
+ [(set (match_operand:QI 0 "reg_operand" "=d,c")
+ (match_operand:QI 1 "not_const_operand" "N,N"))
+ (clobber (reg:CC 21))]
+ ""
+ "@
+ not\\t%N1,%0
+ not\\t%N1,%0"
+ [(set_attr "type" "unarycc,unary")
+ (set_attr "data" "not_uint16,not_uint16")])
+
+; movqi can use this for loading an integer that can't normally
+; fit into a 16-bit signed integer. The drawback is that it cannot
+; go into R0-R11 since that will clobber the CC and movqi shouldn't
+; do that. This can cause additional reloading but in most cases
+; this will cause only an additional register move. With the large
+; memory model we require an extra instruction to load DP anyway,
+; if we're loading the constant from memory. The big advantage of
+; allowing constants that satisfy not_const_operand in movqi, is that
+; it allows andn to be generated more often.
+; However, there is a problem if GCC has decided that it wants
+; to use R0-R11, since we won't have a matching pattern...
+; In interim, we prevent immed_const allowing `N' constants.
+(define_insn "*one_cmplqi2_const_noclobber"
+ [(set (match_operand:QI 0 "std_reg_operand" "=c")
+ (match_operand:QI 1 "not_const_operand" "N"))]
+ ""
+ "not\\t%N1,%0"
+ [(set_attr "type" "unary")
+ (set_attr "data" "not_uint16")])
+
+;
+; ROL
+;
+(define_expand "rotlqi3"
+ [(parallel [(set (match_operand:QI 0 "reg_operand" "")
+ (rotate:QI (match_operand:QI 1 "reg_operand" "")
+ (match_operand:QI 2 "const_int_operand" "")))
+ (clobber (reg:CC 21))])]
+ ""
+ "if (INTVAL (operands[2]) > 4)
+ FAIL; /* Open code as two shifts and an or */
+ if (INTVAL (operands[2]) > 1)
+ {
+ int i;
+ rtx tmp;
+
+ /* If we have 4 or fewer shifts, then it is probably faster
+ to emit separate ROL instructions. A C3x requires
+ at least 4 instructions (a C4x requires at least 3), to
+ perform a rotation by shifts. */
+
+ tmp = operands[1];
+ for (i = 0; i < INTVAL (operands[2]) - 1; i++)
+ {
+ tmp = gen_reg_rtx (QImode);
+ emit_insn (gen_rotl_1_clobber (tmp, operands[1]));
+ operands[1] = tmp;
+ }
+ emit_insn (gen_rotl_1_clobber (operands[0], tmp));
+ DONE;
+ }")
+
+(define_insn "rotl_1_clobber"
+ [(set (match_operand:QI 0 "reg_operand" "=d,c")
+ (rotate:QI (match_operand:QI 1 "reg_operand" "0,0")
+ (const_int 1)))
+ (clobber (reg:CC 21))]
+ ""
+ "rol\\t%0"
+ [(set_attr "type" "unarycc,unary")])
+; Default to int16 data attr.
+
+;
+; ROR
+;
+(define_expand "rotrqi3"
+ [(parallel [(set (match_operand:QI 0 "reg_operand" "")
+ (rotatert:QI (match_operand:QI 1 "reg_operand" "")
+ (match_operand:QI 2 "const_int_operand" "")))
+ (clobber (reg:CC 21))])]
+ ""
+ "if (INTVAL (operands[2]) > 4)
+ FAIL; /* Open code as two shifts and an or */
+ if (INTVAL (operands[2]) > 1)
+ {
+ int i;
+ rtx tmp;
+
+ /* If we have 4 or fewer shifts, then it is probably faster
+ to emit separate ROL instructions. A C3x requires
+ at least 4 instructions (a C4x requires at least 3), to
+ perform a rotation by shifts. */
+
+ tmp = operands[1];
+ for (i = 0; i < INTVAL (operands[2]) - 1; i++)
+ {
+ tmp = gen_reg_rtx (QImode);
+ emit_insn (gen_rotr_1_clobber (tmp, operands[1]));
+ operands[1] = tmp;
+ }
+ emit_insn (gen_rotr_1_clobber (operands[0], tmp));
+ DONE;
+ }")
+
+(define_insn "rotr_1_clobber"
+ [(set (match_operand:QI 0 "reg_operand" "=d,c")
+ (rotatert:QI (match_operand:QI 1 "reg_operand" "0,0")
+ (const_int 1)))
+ (clobber (reg:CC 21))]
+ ""
+ "ror\\t%0"
+ [(set_attr "type" "unarycc,unary")])
+; Default to int16 data attr.
+
+
+;
+; THREE OPERAND INTEGER INSTRUCTIONS
+;
+
+;
+; ADDI
+;
+; This is used by reload when it calls gen_add2_insn for address arithmetic
+; so we must emit the pattern that doesn't clobber CC.
+;
+(define_expand "addqi3"
+ [(parallel [(set (match_operand:QI 0 "reg_operand" "")
+ (plus:QI (match_operand:QI 1 "src_operand" "")
+ (match_operand:QI 2 "src_operand" "")))
+ (clobber (reg:CC_NOOV 21))])]
+ ""
+ "legitimize_operands (PLUS, operands, QImode);
+ if (reload_in_progress
+ || (! IS_PSEUDO_REGNO (operands[0])
+ && ! IS_EXT_REG (REGNO (operands[0]))))
+ {
+ emit_insn (gen_addqi3_noclobber (operands[0], operands[1], operands[2]));
+ DONE;
+ }")
+
+(define_insn "*addqi3_clobber"
+ [(set (match_operand:QI 0 "reg_operand" "=d,d,?d,c,c,?c")
+ (plus:QI (match_operand:QI 1 "src_operand" "%0,rR,rS<>,0,rR,rS<>")
+ (match_operand:QI 2 "src_operand" "rIm,JR,rS<>,rIm,JR,rS<>")))
+ (clobber (reg:CC_NOOV 21))]
+ "valid_operands (PLUS, operands, QImode)"
+ "@
+ addi\\t%2,%0
+ addi3\\t%2,%1,%0
+ addi3\\t%2,%1,%0
+ addi\\t%2,%0
+ addi3\\t%2,%1,%0
+ addi3\\t%2,%1,%0"
+ [(set_attr "type" "binarycc,binarycc,binarycc,binary,binary,binary")])
+; Default to int16 data attr.
+
+(define_insn "*addqi3_test"
+ [(set (reg:CC_NOOV 21)
+ (compare:CC_NOOV (plus:QI (match_operand:QI 1 "src_operand" "%0,rR,rS<>")
+ (match_operand:QI 2 "src_operand" "rIm,JR,rS<>"))
+ (const_int 0)))
+ (clobber (match_scratch:QI 0 "=d,d,d"))]
+ "valid_operands (PLUS, operands, QImode)"
+ "@
+ addi\\t%2,%0
+ addi3\\t%2,%1,%0
+ addi3\\t%2,%1,%0"
+ [(set_attr "type" "binarycc,binarycc,binarycc")])
+; Default to int16 data attr.
+
+; gcc does this in combine.c we just reverse it here
+(define_insn "*cmp_neg"
+ [(set (reg:CC_NOOV 21)
+ (compare:CC_NOOV (match_operand:QI 1 "src_operand" "%0,rR,rS<>")
+ (neg: QI (match_operand:QI 2 "src_operand" "g,JR,rS<>"))))
+ (clobber (match_scratch:QI 0 "=d,d,d"))]
+ "valid_operands (PLUS, operands, QImode)"
+ "@
+ addi\\t%2,%0
+ addi3\\t%2,%1,%0
+ addi3\\t%2,%1,%0"
+ [(set_attr "type" "binarycc,binarycc,binarycc")])
+
+(define_peephole
+ [(parallel [(set (match_operand:QI 0 "ext_reg_operand" "=d,d,d")
+ (plus:QI (match_operand:QI 1 "src_operand" "%0,rR,rS<>")
+ (match_operand:QI 2 "src_operand" "g,JR,rS<>")))
+ (clobber (reg:CC_NOOV 21))])
+ (set (reg:CC_NOOV 21)
+ (compare:CC_NOOV (match_dup 0) (const_int 0)))]
+ "valid_operands (PLUS, operands, QImode)"
+ "@
+ addi\\t%2,%0
+ addi3\\t%2,%1,%0
+ addi3\\t%2,%1,%0"
+ [(set_attr "type" "binarycc,binarycc,binarycc")])
+
+(define_insn "*addqi3_set"
+ [(set (reg:CC_NOOV 21)
+ (compare:CC_NOOV (plus:QI (match_operand:QI 1 "src_operand" "%0,rR,rS<>")
+ (match_operand:QI 2 "src_operand" "rIm,JR,rS<>"))
+ (const_int 0)))
+ (set (match_operand:QI 0 "ext_reg_operand" "=d,d,d")
+ (plus:QI (match_dup 1) (match_dup 2)))]
+ "valid_operands (PLUS, operands, QImode)"
+ "@
+ addi\\t%2,%0
+ addi3\\t%2,%1,%0
+ addi3\\t%2,%1,%0"
+ [(set_attr "type" "binarycc,binarycc,binarycc")])
+; Default to int16 data attr.
+
+; This pattern is required primarily for manipulating the stack pointer
+; where GCC doesn't expect CC to be clobbered or for calculating
+; addresses during reload.
+(define_insn "addqi3_noclobber"
+ [(set (match_operand:QI 0 "std_reg_operand" "=c,c,c")
+ (plus:QI (match_operand:QI 1 "src_operand" "%0,rR,rS<>")
+ (match_operand:QI 2 "src_operand" "rIm,JR,rS<>")))]
+ "valid_operands (PLUS, operands, QImode)"
+ "@
+ addi\\t%2,%0
+ addi3\\t%2,%1,%0
+ addi3\\t%2,%1,%0"
+ [(set_attr "type" "binary,binary,binary")])
+; Default to int16 data attr.
+
+
+; This pattern is required during reload when eliminate_regs_in_insn
+; effectively converts a move insn into an add insn when the src
+; operand is the frame pointer plus a constant. Without this
+; pattern, gen_addqi3 can be called with a register for operand0
+; that can clobber CC.
+; For example, we may have (set (mem (reg ar0)) (reg 99))
+; with (set (reg 99) (plus (reg ar3) (const_int 8)))
+; Now since ar3, the frame pointer, is unchanging within the function,
+; (plus (reg ar3) (const_int 8)) is considered a constant.
+; eliminate_regs_in_insn substitutes this constant to give
+; (set (mem (reg ar0)) (plus (reg ar3) (const_int 8))).
+; This is an invalid C4x insn but if we don't provide a pattern
+; for it, it will be considered to be a move insn for reloading.
+; The nasty bit is that a GENERAL_REGS class register, say r0,
+; may be allocated to reload the PLUS and thus gen_reload will
+; emit an add insn that may clobber CC.
+(define_insn "*addqi3_noclobber_reload"
+ [(set (match_operand:QI 0 "general_operand" "=c,c,c")
+ (plus:QI (match_operand:QI 1 "src_operand" "%0,rR,rS<>")
+ (match_operand:QI 2 "src_operand" "rIm,JR,rS<>")))]
+ "reload_in_progress"
+ "@
+ addi\\t%2,%0
+ addi3\\t%2,%1,%0
+ addi3\\t%2,%1,%0"
+ [(set_attr "type" "binary,binary,binary")])
+; Default to int16 data attr.
+
+
+(define_insn "*addqi3_carry_clobber"
+ [(set (match_operand:QI 0 "reg_operand" "=d,d,?d,c,c,?c")
+ (plus:QI (match_operand:QI 1 "src_operand" "%0,rR,rS<>,0,rR,rS<>")
+ (match_operand:QI 2 "src_operand" "rIm,JR,rS<>,rIm,JR,rS<>")))
+ (use (reg:CC_NOOV 21))
+ (clobber (reg:CC_NOOV 21))]
+ "valid_operands (PLUS, operands, QImode)"
+ "@
+ addc\\t%2,%0
+ addc3\\t%2,%1,%0
+ addc3\\t%2,%1,%0
+ addc\\t%2,%0
+ addc3\\t%2,%1,%0
+ addc3\\t%2,%1,%0"
+ [(set_attr "type" "binarycc,binarycc,binarycc,binary,binary,binary")])
+; Default to int16 data attr.
+
+
+;
+; SUBI/SUBRI
+;
+(define_expand "subqi3"
+ [(parallel [(set (match_operand:QI 0 "reg_operand" "")
+ (minus:QI (match_operand:QI 1 "src_operand" "")
+ (match_operand:QI 2 "src_operand" "")))
+ (clobber (reg:CC_NOOV 21))])]
+ ""
+ "legitimize_operands (MINUS, operands, QImode);")
+
+(define_insn "*subqi3_clobber"
+ [(set (match_operand:QI 0 "reg_operand" "=d,d,d,?d,c,c,c,?c")
+ (minus:QI (match_operand:QI 1 "src_operand" "0,rIm,rR,rS<>,0,rIm,rR,rS<>")
+ (match_operand:QI 2 "src_operand" "rIm,0,JR,rS<>,rIm,0,JR,rS<>")))
+ (clobber (reg:CC_NOOV 21))]
+ "valid_operands (MINUS, operands, QImode)"
+ "@
+ subi\\t%2,%0
+ subri\\t%1,%0
+ subi3\\t%2,%1,%0
+ subi3\\t%2,%1,%0
+ subi\\t%2,%0
+ subri\\t%1,%0
+ subi3\\t%2,%1,%0
+ subi3\\t%2,%1,%0"
+ [(set_attr "type" "binarycc,binarycc,binarycc,binarycc,binary,binary,binary,binary")])
+; Default to int16 data attr.
+
+(define_insn "*subqi3_test"
+ [(set (reg:CC_NOOV 21)
+ (compare:CC_NOOV (minus:QI (match_operand:QI 1 "src_operand" "0,rIm,rR,rS<>")
+ (match_operand:QI 2 "src_operand" "rIm,0,JR,rS<>"))
+ (const_int 0)))
+ (clobber (match_scratch:QI 0 "=d,d,d,?d"))]
+ "valid_operands (MINUS, operands, QImode)"
+ "@
+ subi\\t%2,%0
+ subri\\t%1,%0
+ subi3\\t%2,%1,%0
+ subi3\\t%2,%1,%0"
+ [(set_attr "type" "binarycc,binarycc,binarycc,binarycc")])
+; Default to int16 data attr.
+
+(define_peephole
+ [(parallel [(set (match_operand:QI 0 "ext_reg_operand" "=d,d,d,?d")
+ (minus:QI (match_operand:QI 1 "src_operand" "0,rIm,rR,rS<>")
+ (match_operand:QI 2 "src_operand" "rIm,0,JR,rS<>")))
+ (clobber (reg:CC_NOOV 21))])
+ (set (reg:CC_NOOV 21)
+ (compare:CC_NOOV (match_dup 0) (const_int 0)))]
+ "valid_operands (MINUS, operands, QImode)"
+ "@
+ subi\\t%2,%0
+ subri\\t%1,%0
+ subi3\\t%2,%1,%0
+ subi3\\t%2,%1,%0"
+ [(set_attr "type" "binarycc,binarycc,binarycc,binarycc")])
+
+(define_insn "*subqi3_set"
+ [(set (reg:CC_NOOV 21)
+ (compare:CC_NOOV (minus:QI (match_operand:QI 1 "src_operand" "0,rIm,rR,rS<>")
+ (match_operand:QI 2 "src_operand" "rIm,0,JR,rS<>"))
+ (const_int 0)))
+ (set (match_operand:QI 0 "ext_reg_operand" "=d,d,d,?d")
+ (minus:QI (match_dup 1)
+ (match_dup 2)))]
+ "valid_operands (MINUS, operands, QImode)"
+ "@
+ subi\\t%2,%0
+ subri\\t%1,%0
+ subi3\\t%2,%1,%0
+ subi3\\t%2,%1,%0"
+ [(set_attr "type" "binarycc,binarycc,binarycc,binarycc")])
+; Default to int16 data attr.
+
+(define_insn "*subqi3_carry_clobber"
+ [(set (match_operand:QI 0 "reg_operand" "=d,d,d,?d,c,c,c,?c")
+ (minus:QI (match_operand:QI 1 "src_operand" "0,rIm,rR,rS<>,0,rIm,rR,rS<>")
+ (match_operand:QI 2 "src_operand" "rIm,0,JR,rS<>,rIm,0,JR,rS<>")))
+ (use (reg:CC_NOOV 21))
+ (clobber (reg:CC_NOOV 21))]
+ "valid_operands (MINUS, operands, QImode)"
+ "@
+ subb\\t%2,%0
+ subrb\\t%1,%0
+ subb3\\t%2,%1,%0
+ subb3\\t%2,%1,%0
+ subb\\t%2,%0
+ subrb\\t%1,%0
+ subb3\\t%2,%1,%0
+ subb3\\t%2,%1,%0"
+ [(set_attr "type" "binarycc,binarycc,binarycc,binarycc,binary,binary,binary,binary")])
+; Default to int16 data attr.
+
+(define_insn "*subqi3_carry_set"
+ [(set (reg:CC_NOOV 21)
+ (compare:CC_NOOV (minus:QI (match_operand:QI 1 "src_operand" "0,rIm,rR,rS<>")
+ (match_operand:QI 2 "src_operand" "rIm,0,JR,rS<>"))
+ (const_int 0)))
+ (set (match_operand:QI 0 "ext_reg_operand" "=d,d,d,?d")
+ (minus:QI (match_dup 1)
+ (match_dup 2)))
+ (use (reg:CC_NOOV 21))]
+ "valid_operands (MINUS, operands, QImode)"
+ "@
+ subb\\t%2,%0
+ subrb\\t%1,%0
+ subb3\\t%2,%1,%0
+ subb3\\t%2,%1,%0"
+ [(set_attr "type" "binarycc,binarycc,binarycc,binarycc")])
+; Default to int16 data attr.
+
+;
+; MPYI
+;
+(define_expand "mulqi3"
+ [(parallel [(set (match_operand:QI 0 "reg_operand" "")
+ (mult:QI (match_operand:QI 1 "src_operand" "")
+ (match_operand:QI 2 "src_operand" "")))
+ (clobber (reg:CC_NOOV 21))])]
+ ""
+ "if (TARGET_MPYI || (GET_CODE (operands[2]) == CONST_INT
+ && exact_log2 (INTVAL (operands[2])) >= 0))
+ legitimize_operands (MULT, operands, QImode);
+ else
+ {
+ if (GET_CODE (operands[2]) == CONST_INT)
+ {
+ /* Let GCC try to synthesise the multiplication using shifts
+ and adds. In most cases this will be more profitable than
+ using the C3x MPYI. */
+ FAIL;
+ }
+ if (operands[1] == operands[2])
+ {
+ /* Do the squaring operation in-line. */
+ emit_insn (gen_sqrqi2_inline (operands[0], operands[1]));
+ DONE;
+ }
+ if (TARGET_INLINE)
+ {
+ emit_insn (gen_mulqi3_inline (operands[0], operands[1],
+ operands[2]));
+ DONE;
+ }
+ c4x_emit_libcall3 (MULQI3_LIBCALL, MULT, QImode, operands);
+ DONE;
+ }
+ ")
+
+(define_insn "*mulqi3_clobber"
+ [(set (match_operand:QI 0 "reg_operand" "=d,d,?d,c,c,?c")
+ (mult:QI (match_operand:QI 1 "src_operand" "%0,rR,rS<>,0,rR,rS<>")
+ (match_operand:QI 2 "src_operand" "rIm,JR,rS<>,rIm,JR,rS<>")))
+ (clobber (reg:CC_NOOV 21))]
+ "valid_operands (MULT, operands, QImode)"
+ "*
+ if (which_alternative == 0 || which_alternative == 3)
+ {
+ if (TARGET_C3X
+ && GET_CODE (operands[2]) == CONST_INT
+ && exact_log2 (INTVAL (operands[2])) >= 0)
+ return \"ash\\t%L2,%0\";
+ else
+ return \"mpyi\\t%2,%0\";
+ }
+ else
+ return \"mpyi3\\t%2,%1,%0\";"
+ [(set_attr "type" "binarycc,binarycc,binarycc,binary,binary,binary")])
+; Default to int16 data attr.
+
+(define_insn "*mulqi3_test"
+ [(set (reg:CC_NOOV 21)
+ (compare:CC_NOOV (mult:QI (match_operand:QI 1 "src_operand" "%0,rR,rS<>")
+ (match_operand:QI 2 "src_operand" "rIm,JR,rS<>"))
+ (const_int 0)))
+ (clobber (match_scratch:QI 0 "=d,d,d"))]
+ "valid_operands (MULT, operands, QImode)"
+ "*
+ if (which_alternative == 0)
+ {
+ if (TARGET_C3X
+ && GET_CODE (operands[2]) == CONST_INT
+ && exact_log2 (INTVAL (operands[2])) >= 0)
+ return \"ash\\t%L2,%0\";
+ else
+ return \"mpyi\\t%2,%0\";
+ }
+ else
+ return \"mpyi3\\t%2,%1,%0\";"
+ [(set_attr "type" "binarycc,binarycc,binarycc")])
+; Default to int16 data attr.
+
+(define_insn "*mulqi3_set"
+ [(set (reg:CC_NOOV 21)
+ (compare:CC_NOOV (mult:QI (match_operand:QI 1 "src_operand" "%0,rR,rS<>")
+ (match_operand:QI 2 "src_operand" "rIm,JR,rS<>"))
+ (const_int 0)))
+ (set (match_operand:QI 0 "ext_reg_operand" "=d,d,d")
+ (mult:QI (match_dup 1)
+ (match_dup 2)))]
+ "valid_operands (MULT, operands, QImode)"
+ "*
+ if (which_alternative == 0)
+ {
+ if (TARGET_C3X
+ && GET_CODE (operands[2]) == CONST_INT
+ && exact_log2 (INTVAL (operands[2])) >= 0)
+ return \"ash\\t%L2,%0\";
+ else
+ return \"mpyi\\t%2,%0\";
+ }
+ else
+ return \"mpyi3\\t%2,%1,%0\";"
+ [(set_attr "type" "binarycc,binarycc,binarycc")])
+; Default to int16 data attr.
+
+; The C3x multiply instruction assumes 24-bit signed integer operands
+; and the 48-bit result is truncated to 32-bits.
+(define_insn "*mulqi3_24_clobber"
+ [(set (match_operand:QI 0 "reg_operand" "=d,d,?d,c,c,?c")
+ (mult:QI
+ (sign_extend:QI
+ (and:QI (match_operand:QI 1 "src_operand" "%0,rR,rS<>,0,rR,rS<>")
+ (const_int 16777215)))
+ (sign_extend:QI
+ (and:QI (match_operand:QI 2 "src_operand" "rIm,JR,rS<>,rIm,JR,rS<>")
+ (const_int 16777215)))))
+ (clobber (reg:CC_NOOV 21))]
+ "TARGET_C3X && valid_operands (MULT, operands, QImode)"
+ "@
+ mpyi\\t%2,%0
+ mpyi3\\t%2,%1,%0
+ mpyi3\\t%2,%1,%0
+ mpyi\\t%2,%0
+ mpyi3\\t%2,%1,%0
+ mpyi3\\t%2,%1,%0"
+ [(set_attr "type" "binarycc,binarycc,binarycc,binary,binary,binary")])
+; Default to int16 data attr.
+
+
+; Fast square function for C3x where TARGET_MPYI not asserted
+(define_expand "sqrqi2_inline"
+ [(set (match_dup 7) (match_operand:QI 1 "src_operand" ""))
+ (parallel [(set (match_dup 3)
+ (lshiftrt:QI (match_dup 7) (const_int 16)))
+ (clobber (reg:CC 21))])
+ (parallel [(set (match_dup 2)
+ (and:QI (match_dup 7) (const_int 65535)))
+ (clobber (reg:CC 21))])
+ (parallel [(set (match_dup 4)
+ (mult:QI (sign_extend:QI (and:QI (match_dup 2)
+ (const_int 16777215)))
+ (sign_extend:QI (and:QI (match_dup 2)
+ (const_int 16777215)))))
+ (clobber (reg:CC_NOOV 21))])
+ (parallel [(set (match_dup 5)
+ (mult:QI (sign_extend:QI (and:QI (match_dup 2)
+ (const_int 16777215)))
+ (sign_extend:QI (and:QI (match_dup 3)
+ (const_int 16777215)))))
+ (clobber (reg:CC_NOOV 21))])
+ (parallel [(set (match_dup 6)
+ (ashift:QI (match_dup 5) (const_int 17)))
+ (clobber (reg:CC 21))])
+ (parallel [(set (match_operand:QI 0 "reg_operand" "")
+ (plus:QI (match_dup 4) (match_dup 6)))
+ (clobber (reg:CC_NOOV 21))])]
+ ""
+ "
+ operands[2] = gen_reg_rtx (QImode); /* a = val & 0xffff */
+ operands[3] = gen_reg_rtx (QImode); /* b = val >> 16 */
+ operands[4] = gen_reg_rtx (QImode); /* a * a */
+ operands[5] = gen_reg_rtx (QImode); /* a * b */
+ operands[6] = gen_reg_rtx (QImode); /* (a * b) << 17 */
+ operands[7] = gen_reg_rtx (QImode); /* val */
+ ")
+
+; Inlined integer multiply for C3x
+(define_expand "mulqi3_inline"
+ [(set (match_dup 12) (const_int -16))
+ (set (match_dup 13) (match_operand:QI 1 "src_operand" ""))
+ (set (match_dup 14) (match_operand:QI 2 "src_operand" ""))
+ (parallel [(set (match_dup 4)
+ (lshiftrt:QI (match_dup 13) (neg:QI (match_dup 12))))
+ (clobber (reg:CC 21))])
+ (parallel [(set (match_dup 6)
+ (lshiftrt:QI (match_dup 14) (neg:QI (match_dup 12))))
+ (clobber (reg:CC 21))])
+ (parallel [(set (match_dup 3)
+ (and:QI (match_dup 13)
+ (const_int 65535)))
+ (clobber (reg:CC 21))])
+ (parallel [(set (match_dup 5)
+ (and:QI (match_dup 14)
+ (const_int 65535)))
+ (clobber (reg:CC 21))])
+ (parallel [(set (match_dup 7)
+ (mult:QI (sign_extend:QI (and:QI (match_dup 4)
+ (const_int 16777215)))
+ (sign_extend:QI (and:QI (match_dup 5)
+ (const_int 16777215)))))
+ (clobber (reg:CC_NOOV 21))])
+ (parallel [(set (match_dup 8)
+ (mult:QI (sign_extend:QI (and:QI (match_dup 3)
+ (const_int 16777215)))
+ (sign_extend:QI (and:QI (match_dup 5)
+ (const_int 16777215)))))
+ (clobber (reg:CC_NOOV 21))])
+ (parallel [(set (match_dup 9)
+ (mult:QI (sign_extend:QI (and:QI (match_dup 3)
+ (const_int 16777215)))
+ (sign_extend:QI (and:QI (match_dup 6)
+ (const_int 16777215)))))
+ (clobber (reg:CC_NOOV 21))])
+ (parallel [(set (match_dup 10)
+ (plus:QI (match_dup 7) (match_dup 9)))
+ (clobber (reg:CC_NOOV 21))])
+ (parallel [(set (match_dup 11)
+ (ashift:QI (match_dup 10) (const_int 16)))
+ (clobber (reg:CC 21))])
+ (parallel [(set (match_operand:QI 0 "reg_operand" "")
+ (plus:QI (match_dup 8) (match_dup 11)))
+ (clobber (reg:CC_NOOV 21))])]
+ "TARGET_C3X"
+ "
+ operands[3] = gen_reg_rtx (QImode); /* a = arg1 & 0xffff */
+ operands[4] = gen_reg_rtx (QImode); /* b = arg1 >> 16 */
+ operands[5] = gen_reg_rtx (QImode); /* a = arg2 & 0xffff */
+ operands[6] = gen_reg_rtx (QImode); /* b = arg2 >> 16 */
+ operands[7] = gen_reg_rtx (QImode); /* b * c */
+ operands[8] = gen_reg_rtx (QImode); /* a * c */
+ operands[9] = gen_reg_rtx (QImode); /* a * d */
+ operands[10] = gen_reg_rtx (QImode); /* b * c + a * d */
+ operands[11] = gen_reg_rtx (QImode); /* (b *c + a * d) << 16 */
+ operands[12] = gen_reg_rtx (QImode); /* -16 */
+ operands[13] = gen_reg_rtx (QImode); /* arg1 */
+ operands[14] = gen_reg_rtx (QImode); /* arg2 */
+ ")
+
+;
+; MPYSHI (C4x only)
+;
+(define_expand "smulqi3_highpart"
+ [(parallel [(set (match_operand:QI 0 "reg_operand" "")
+ (truncate:QI
+ (lshiftrt:HI
+ (mult:HI
+ (sign_extend:HI (match_operand:QI 1 "src_operand" ""))
+ (sign_extend:HI (match_operand:QI 2 "src_operand" "")))
+ (const_int 32))))
+ (clobber (reg:CC_NOOV 21))])]
+ ""
+ "legitimize_operands (MULT, operands, QImode);
+ if (TARGET_C3X)
+ {
+ c4x_emit_libcall_mulhi (SMULHI3_LIBCALL, SIGN_EXTEND, QImode, operands);
+ DONE;
+ }
+ ")
+
+(define_insn "*smulqi3_highpart_clobber"
+ [(set (match_operand:QI 0 "reg_operand" "=d,d,?d,c,c,?c")
+ (truncate:QI
+ (lshiftrt:HI
+ (mult:HI
+ (sign_extend:HI (match_operand:QI 1 "src_operand" "%0,rR,rS<>,0,rR,rS<>"))
+ (sign_extend:HI (match_operand:QI 2 "src_operand" "rIm,JR,rS<>,rIm,JR,rS<>")))
+ (const_int 32))))
+ (clobber (reg:CC_NOOV 21))]
+ "! TARGET_C3X && valid_operands (MULT, operands, QImode)"
+ "@
+ mpyshi\\t%2,%0
+ mpyshi3\\t%2,%1,%0
+ mpyshi3\\t%2,%1,%0
+ mpyshi\\t%2,%0
+ mpyshi3\\t%2,%1,%0
+ mpyshi3\\t%2,%1,%0"
+ [(set_attr "type" "binarycc,binarycc,binarycc,binary,binary,binary")
+ (set_attr "data" "int16,int16,int16,int16,int16,int16")])
+
+;
+; MPYUHI (C4x only)
+;
+(define_expand "umulqi3_highpart"
+ [(parallel [(set (match_operand:QI 0 "reg_operand" "")
+ (truncate:QI
+ (lshiftrt:HI
+ (mult:HI
+ (zero_extend:HI (match_operand:QI 1 "src_operand" ""))
+ (zero_extend:HI (match_operand:QI 2 "lsrc_operand" "")))
+ (const_int 32))))
+ (clobber (reg:CC_NOOV 21))])]
+ ""
+ "legitimize_operands (MULT, operands, QImode);
+ if (TARGET_C3X)
+ {
+ c4x_emit_libcall_mulhi (UMULHI3_LIBCALL, ZERO_EXTEND, QImode, operands);
+ DONE;
+ }
+ ")
+
+(define_insn "*umulqi3_highpart_clobber"
+ [(set (match_operand:QI 0 "reg_operand" "=d,d,?d,c,c,?c")
+ (truncate:QI
+ (lshiftrt:HI
+ (mult:HI
+ (zero_extend:HI (match_operand:QI 1 "src_operand" "%0,rR,rS<>,0,rR,rS<>"))
+ (zero_extend:HI (match_operand:QI 2 "lsrc_operand" "rLm,JR,rS<>,rLm,JR,rS<>")))
+ (const_int 32))))
+ (clobber (reg:CC_NOOV 21))]
+ "! TARGET_C3X && valid_operands (MULT, operands, QImode)"
+ "@
+ mpyuhi\\t%2,%0
+ mpyuhi3\\t%2,%1,%0
+ mpyuhi3\\t%2,%1,%0
+ mpyuhi\\t%2,%0
+ mpyuhi3\\t%2,%1,%0
+ mpyuhi3\\t%2,%1,%0"
+ [(set_attr "type" "binarycc,binarycc,binarycc,binary,binary,binary")
+ (set_attr "data" "uint16,uint16,uint16,uint16,uint16,uint16")])
+
+;
+; AND
+;
+(define_expand "andqi3"
+ [(parallel [(set (match_operand:QI 0 "reg_operand" "")
+ (and:QI (match_operand:QI 1 "src_operand" "")
+ (match_operand:QI 2 "tsrc_operand" "")))
+ (clobber (reg:CC 21))])]
+ ""
+ "legitimize_operands (AND, operands, QImode);")
+
+
+(define_insn "*andqi3_255_clobber"
+ [(set (match_operand:QI 0 "reg_operand" "=d,c")
+ (and:QI (match_operand:QI 1 "src_operand" "mr,mr")
+ (const_int 255)))
+ (clobber (reg:CC 21))]
+ "! TARGET_C3X"
+ "lbu0\\t%1,%0"
+ [(set_attr "type" "unarycc")])
+
+
+(define_insn "*andqi3_65535_clobber"
+ [(set (match_operand:QI 0 "reg_operand" "=d,c")
+ (and:QI (match_operand:QI 1 "src_operand" "mr,mr")
+ (const_int 65535)))
+ (clobber (reg:CC 21))]
+ "! TARGET_C3X"
+ "lhu0\\t%1,%0"
+ [(set_attr "type" "unarycc")])
+
+
+(define_insn "*andqi3_clobber"
+ [(set (match_operand:QI 0 "reg_operand" "=d,d,d,?d,c,c,c,?c")
+ (and:QI (match_operand:QI 1 "src_operand" "%0,0,rR,rS<>,0,0,rR,rS<>")
+ (match_operand:QI 2 "tsrc_operand" "N,rLm,JR,rS<>,N,rLm,JR,rS<>")))
+ (clobber (reg:CC 21))]
+ "valid_operands (AND, operands, QImode)"
+ "@
+ andn\\t%N2,%0
+ and\\t%2,%0
+ and3\\t%2,%1,%0
+ and3\\t%2,%1,%0
+ andn\\t%N2,%0
+ and\\t%2,%0
+ and3\\t%2,%1,%0
+ and3\\t%2,%1,%0"
+ [(set_attr "type" "binarycc,binarycc,binarycc,binarycc,binary,binary,binary,binary")
+ (set_attr "data" "not_uint16,uint16,int16,uint16,not_uint16,uint16,int16,uint16")])
+
+(define_insn "*andqi3_test"
+ [(set (reg:CC 21)
+ (compare:CC (and:QI (match_operand:QI 1 "src_operand" "%0,r,rR,rS<>")
+ (match_operand:QI 2 "tsrc_operand" "N,rLm,JR,rS<>"))
+ (const_int 0)))
+ (clobber (match_scratch:QI 0 "=d,X,X,?X"))]
+ "valid_operands (AND, operands, QImode)"
+ "@
+ andn\\t%N2,%0
+ tstb\\t%2,%1
+ tstb3\\t%2,%1
+ tstb3\\t%2,%1"
+ [(set_attr "type" "binarycc,binarycc,binarycc,binarycc")
+ (set_attr "data" "not_uint16,uint16,int16,uint16")])
+
+(define_peephole
+ [(parallel [(set (match_operand:QI 0 "ext_reg_operand" "=d,d,d,?d")
+ (and:QI (match_operand:QI 1 "src_operand" "%0,0,rR,rS<>")
+ (match_operand:QI 2 "tsrc_operand" "N,rLm,JR,rS<>")))
+ (clobber (reg:CC 21))])
+ (set (reg:CC 21)
+ (compare:CC (match_dup 0) (const_int 0)))]
+ "valid_operands (AND, operands, QImode)"
+ "@
+ andn\\t%N2,%0
+ and\\t%2,%0
+ and3\\t%2,%1,%0
+ and3\\t%2,%1,%0"
+ [(set_attr "type" "binarycc,binarycc,binarycc,binarycc")
+ (set_attr "data" "not_uint16,uint16,int16,uint16")])
+
+(define_insn "*andqi3_set"
+ [(set (reg:CC 21)
+ (compare:CC (and:QI (match_operand:QI 1 "src_operand" "%0,0,rR,rS<>")
+ (match_operand:QI 2 "tsrc_operand" "N,rLm,JR,rS<>"))
+ (const_int 0)))
+ (set (match_operand:QI 0 "ext_reg_operand" "=d,d,d,?d")
+ (and:QI (match_dup 1)
+ (match_dup 2)))]
+ "valid_operands (AND, operands, QImode)"
+ "@
+ andn\\t%N2,%0
+ and\\t%2,%0
+ and3\\t%2,%1,%0
+ and3\\t%2,%1,%0"
+ [(set_attr "type" "binarycc,binarycc,binarycc,binarycc")
+ (set_attr "data" "not_uint16,uint16,int16,uint16")])
+
+;
+; ANDN
+;
+; NB, this insn doesn't have commutative operands, but valid_operands
+; assumes that the code AND does. We might have to kludge this if
+; we make valid_operands stricter.
+(define_insn "*andnqi3_clobber"
+ [(set (match_operand:QI 0 "reg_operand" "=d,d,?d,c,c,?c")
+ (and:QI (not:QI (match_operand:QI 2 "lsrc_operand" "rLm,JR,rS<>,rLm,JR,rS<>"))
+ (match_operand:QI 1 "src_operand" "0,rR,rS<>,0,rR,rS<>")))
+ (clobber (reg:CC 21))]
+ "valid_operands (AND, operands, QImode)"
+ "@
+ andn\\t%2,%0
+ andn3\\t%2,%1,%0
+ andn3\\t%2,%1,%0
+ andn\\t%2,%0
+ andn3\\t%2,%1,%0
+ andn3\\t%2,%1,%0"
+ [(set_attr "type" "binarycc,binarycc,binarycc,binary,binary,binary")
+ (set_attr "data" "uint16,int16,uint16,uint16,int16,uint16")])
+
+(define_insn "*andnqi3_test"
+ [(set (reg:CC 21)
+ (compare:CC (and:QI (not:QI (match_operand:QI 2 "lsrc_operand" "rLm,JR,rS<>"))
+ (match_operand:QI 1 "src_operand" "0,rR,rS<>"))
+ (const_int 0)))
+ (clobber (match_scratch:QI 0 "=d,d,d"))]
+ "valid_operands (AND, operands, QImode)"
+ "@
+ andn\\t%2,%0
+ andn3\\t%2,%1,%0
+ andn3\\t%2,%1,%0"
+ [(set_attr "type" "binarycc,binarycc,binarycc")
+ (set_attr "data" "uint16,int16,uint16")])
+
+(define_insn "*andnqi3_set"
+ [(set (reg:CC 21)
+ (compare:CC (and:QI (not:QI (match_operand:QI 2 "lsrc_operand" "rLm,JR,rS<>"))
+ (match_operand:QI 1 "src_operand" "0,rR,rS<>"))
+ (const_int 0)))
+ (set (match_operand:QI 0 "ext_reg_operand" "=d,d,d")
+ (and:QI (not:QI (match_dup 2))
+ (match_dup 1)))]
+ "valid_operands (AND, operands, QImode)"
+ "@
+ andn\\t%2,%0
+ andn3\\t%2,%1,%0
+ andn3\\t%2,%1,%0"
+ [(set_attr "type" "binarycc,binarycc,binarycc")
+ (set_attr "data" "uint16,int16,uint16")])
+
+;
+; OR
+;
+(define_expand "iorqi3"
+ [(parallel [(set (match_operand:QI 0 "reg_operand" "")
+ (ior:QI (match_operand:QI 1 "src_operand" "")
+ (match_operand:QI 2 "lsrc_operand" "")))
+ (clobber (reg:CC 21))])]
+ ""
+ "legitimize_operands (IOR, operands, QImode);")
+
+(define_insn "*iorqi3_clobber"
+ [(set (match_operand:QI 0 "reg_operand" "=d,d,?d,c,c,?c")
+ (ior:QI (match_operand:QI 1 "src_operand" "%0,rR,rS<>,0,rR,rS<>")
+ (match_operand:QI 2 "lsrc_operand" "rLm,JR,rS<>,rLm,JR,rS<>")))
+ (clobber (reg:CC 21))]
+ "valid_operands (IOR, operands, QImode)"
+ "@
+ or\\t%2,%0
+ or3\\t%2,%1,%0
+ or3\\t%2,%1,%0
+ or\\t%2,%0
+ or3\\t%2,%1,%0
+ or3\\t%2,%1,%0"
+ [(set_attr "type" "binarycc,binarycc,binarycc,binary,binary,binary")
+ (set_attr "data" "uint16,int16,uint16,uint16,int16,uint16")])
+
+(define_insn "*iorqi3_test"
+ [(set (reg:CC 21)
+ (compare:CC (ior:QI (match_operand:QI 1 "src_operand" "%0,rR,rS<>")
+ (match_operand:QI 2 "lsrc_operand" "rLm,JR,rS<>"))
+ (const_int 0)))
+ (clobber (match_scratch:QI 0 "=d,d,d"))]
+ "valid_operands (IOR, operands, QImode)"
+ "@
+ or\\t%2,%0
+ or3\\t%2,%1,%0
+ or3\\t%2,%1,%0"
+ [(set_attr "type" "binarycc,binarycc,binarycc")
+ (set_attr "data" "uint16,int16,uint16")])
+
+(define_peephole
+ [(parallel [(set (match_operand:QI 0 "ext_reg_operand" "=d,d,d")
+ (ior:QI (match_operand:QI 1 "src_operand" "%0,rR,rS<>")
+ (match_operand:QI 2 "lsrc_operand" "rLm,JR,rS<>")))
+ (clobber (reg:CC 21))])
+ (set (reg:CC 21)
+ (compare:CC (match_dup 0) (const_int 0)))]
+ "valid_operands (IOR, operands, QImode)"
+ "@
+ or\\t%2,%0
+ or3\\t%2,%1,%0
+ or3\\t%2,%1,%0"
+ [(set_attr "type" "binarycc,binarycc,binarycc")
+ (set_attr "data" "uint16,int16,uint16")])
+
+(define_insn "*iorqi3_set"
+ [(set (reg:CC 21)
+ (compare:CC (ior:QI (match_operand:QI 1 "src_operand" "%0,rR,rS<>")
+ (match_operand:QI 2 "lsrc_operand" "rLm,JR,rS<>"))
+ (const_int 0)))
+ (set (match_operand:QI 0 "ext_reg_operand" "=d,d,d")
+ (ior:QI (match_dup 1)
+ (match_dup 2)))]
+ "valid_operands (IOR, operands, QImode)"
+ "@
+ or\\t%2,%0
+ or3\\t%2,%1,%0
+ or3\\t%2,%1,%0"
+ [(set_attr "type" "binarycc,binarycc,binarycc")
+ (set_attr "data" "uint16,int16,uint16")])
+
+; This pattern is used for loading symbol references in several parts.
+(define_insn "iorqi3_noclobber"
+ [(set (match_operand:QI 0 "std_reg_operand" "=c,c,c")
+ (ior:QI (match_operand:QI 1 "src_operand" "%0,rR,rS<>")
+ (match_operand:QI 2 "lsrc_operand" "rLm,JR,rS<>")))]
+ "valid_operands (IOR, operands, QImode)"
+ "@
+ or\\t%2,%0
+ or3\\t%2,%1,%0
+ or3\\t%2,%1,%0"
+ [(set_attr "type" "binary,binary,binary")
+ (set_attr "data" "uint16,int16,uint16")])
+
+;
+; XOR
+;
+(define_expand "xorqi3"
+ [(parallel [(set (match_operand:QI 0 "reg_operand" "")
+ (xor:QI (match_operand:QI 1 "src_operand" "")
+ (match_operand:QI 2 "lsrc_operand" "")))
+ (clobber (reg:CC 21))])]
+ ""
+ "legitimize_operands (XOR, operands, QImode);")
+
+(define_insn "*xorqi3_clobber"
+ [(set (match_operand:QI 0 "reg_operand" "=d,d,?d,c,c,?c")
+ (xor:QI (match_operand:QI 1 "src_operand" "%0,rR,rS<>,0,rR,rS<>")
+ (match_operand:QI 2 "lsrc_operand" "rLm,JR,rS<>,rLm,JR,rS<>")))
+ (clobber (reg:CC 21))]
+ "valid_operands (XOR, operands, QImode)"
+ "@
+ xor\\t%2,%0
+ xor3\\t%2,%1,%0
+ xor3\\t%2,%1,%0
+ xor\\t%2,%0
+ xor3\\t%2,%1,%0
+ xor3\\t%2,%1,%0"
+ [(set_attr "type" "binarycc,binarycc,binarycc,binary,binary,binary")
+ (set_attr "data" "uint16,int16,uint16,uint16,int16,uint16")])
+
+(define_insn "*xorqi3_test"
+ [(set (reg:CC 21)
+ (compare:CC (xor:QI (match_operand:QI 1 "src_operand" "%0,rR,rS<>")
+ (match_operand:QI 2 "lsrc_operand" "rLm,JR,rS<>"))
+ (const_int 0)))
+ (clobber (match_scratch:QI 0 "=d,d,d"))]
+ "valid_operands (XOR, operands, QImode)"
+ "@
+ xor\\t%2,%0
+ xor3\\t%2,%1,%0
+ xor3\\t%2,%1,%0"
+ [(set_attr "type" "binarycc,binarycc,binarycc")
+ (set_attr "data" "uint16,int16,uint16")])
+
+(define_insn "*xorqi3_set"
+ [(set (reg:CC 21)
+ (compare:CC (xor:QI (match_operand:QI 1 "src_operand" "%0,rR,rS<>")
+ (match_operand:QI 2 "lsrc_operand" "rLm,JR,rS<>"))
+ (const_int 0)))
+ (set (match_operand:QI 0 "ext_reg_operand" "=d,d,d")
+ (xor:QI (match_dup 1)
+ (match_dup 2)))]
+ "valid_operands (XOR, operands, QImode)"
+ "@
+ xor\\t%2,%0
+ xor3\\t%2,%1,%0
+ xor3\\t%2,%1,%0"
+ [(set_attr "type" "binarycc,binarycc,binarycc")
+ (set_attr "data" "uint16,int16,uint16")])
+
+;
+; LSH/ASH (left)
+;
+; The C3x and C4x have two shift instructions ASH and LSH
+; If the shift count is positive, a left shift is performed
+; otherwise a right shift is performed. The number of bits
+; shifted is determined by the seven LSBs of the shift count.
+; If the absolute value of the count is 32 or greater, the result
+; using the LSH instruction is zero; with the ASH insn the result
+; is zero or negative 1. Note that the ISO C standard allows
+; the result to be machine dependent whenever the shift count
+; exceeds the size of the object.
+(define_expand "ashlqi3"
+ [(parallel [(set (match_operand:QI 0 "reg_operand" "")
+ (ashift:QI (match_operand:QI 1 "src_operand" "")
+ (match_operand:QI 2 "src_operand" "")))
+ (clobber (reg:CC 21))])]
+ ""
+ "legitimize_operands (ASHIFT, operands, QImode);")
+
+(define_insn "*ashlqi3_clobber"
+ [(set (match_operand:QI 0 "reg_operand" "=d,d,?d,c,c,?c")
+ (ashift:QI (match_operand:QI 1 "src_operand" "0,rR,rS<>,0,rR,rS<>")
+ (match_operand:QI 2 "src_operand" "rIm,JR,rS<>,rIm,JR,rS<>")))
+ (clobber (reg:CC 21))]
+ "valid_operands (ASHIFT, operands, QImode)"
+ "@
+ ash\\t%2,%0
+ ash3\\t%2,%1,%0
+ ash3\\t%2,%1,%0
+ ash\\t%2,%0
+ ash3\\t%2,%1,%0
+ ash3\\t%2,%1,%0"
+ [(set_attr "type" "binarycc,binarycc,binarycc,binary,binary,binary")])
+; Default to int16 data attr.
+
+(define_insn "*ashlqi3_set"
+ [(set (reg:CC 21)
+ (compare:CC
+ (ashift:QI (match_operand:QI 1 "src_operand" "0,rR,rS<>")
+ (match_operand:QI 2 "src_operand" "rIm,JR,rS<>"))
+ (const_int 0)))
+ (set (match_operand:QI 0 "reg_operand" "=d,d,d")
+ (ashift:QI (match_dup 1)
+ (match_dup 2)))]
+ "valid_operands (ASHIFT, operands, QImode)"
+ "@
+ ash\\t%2,%0
+ ash3\\t%2,%1,%0
+ ash3\\t%2,%1,%0"
+ [(set_attr "type" "binarycc,binarycc,binarycc")])
+; Default to int16 data attr.
+
+; This is only used by lshrhi3_reg where we need a LSH insn that will
+; shift both ways.
+(define_insn "*lshlqi3_clobber"
+ [(set (match_operand:QI 0 "reg_operand" "=d,d,?d,c,c,?c")
+ (ashift:QI (match_operand:QI 1 "src_operand" "0,rR,rS<>,0,rR,rS<>")
+ (unspec [(match_operand:QI 2 "src_operand" "rIm,JR,rS<>,rIm,JR,rS<>")] 3)))
+ (clobber (reg:CC 21))]
+ "valid_operands (ASHIFT, operands, QImode)"
+ "@
+ lsh\\t%2,%0
+ lsh3\\t%2,%1,%0
+ lsh3\\t%2,%1,%0
+ lsh\\t%2,%0
+ lsh3\\t%2,%1,%0
+ lsh3\\t%2,%1,%0"
+ [(set_attr "type" "binarycc,binarycc,binarycc,binary,binary,binary")])
+; Default to int16 data attr.
+
+;
+; LSH (right)
+;
+; Logical right shift on the C[34]x works by negating the shift count,
+; then emitting a right shift with the shift count negated. This means
+; that all actual shift counts in the RTL will be positive.
+;
+(define_expand "lshrqi3"
+ [(parallel [(set (match_operand:QI 0 "reg_operand" "")
+ (lshiftrt:QI (match_operand:QI 1 "src_operand" "")
+ (match_operand:QI 2 "src_operand" "")))
+ (clobber (reg:CC 21))])]
+ ""
+ "legitimize_operands (LSHIFTRT, operands, QImode);")
+
+
+(define_insn "*lshrqi3_24_clobber"
+ [(set (match_operand:QI 0 "reg_operand" "=d,c")
+ (lshiftrt:QI (match_operand:QI 1 "src_operand" "mr,mr")
+ (const_int 24)))
+ (clobber (reg:CC 21))]
+ "! TARGET_C3X"
+ "lbu3\\t%1,%0"
+ [(set_attr "type" "unarycc")])
+
+
+(define_insn "*ashrqi3_24_clobber"
+ [(set (match_operand:QI 0 "reg_operand" "=d,c")
+ (ashiftrt:QI (match_operand:QI 1 "src_operand" "mr,mr")
+ (const_int 24)))
+ (clobber (reg:CC 21))]
+ "! TARGET_C3X"
+ "lb3\\t%1,%0"
+ [(set_attr "type" "unarycc")])
+
+
+(define_insn "lshrqi3_16_clobber"
+ [(set (match_operand:QI 0 "reg_operand" "=d,c")
+ (lshiftrt:QI (match_operand:QI 1 "src_operand" "mr,mr")
+ (const_int 16)))
+ (clobber (reg:CC 21))]
+ "! TARGET_C3X"
+ "lhu1\\t%1,%0"
+ [(set_attr "type" "unarycc")])
+
+
+(define_insn "*ashrqi3_16_clobber"
+ [(set (match_operand:QI 0 "reg_operand" "=d,c")
+ (ashiftrt:QI (match_operand:QI 1 "src_operand" "mr,mr")
+ (const_int 16)))
+ (clobber (reg:CC 21))]
+ "! TARGET_C3X"
+ "lh1\\t%1,%0"
+ [(set_attr "type" "unarycc")])
+
+
+; When the shift count is greater than the size of the word
+; the result can be implementation specific
+(define_insn "*lshrqi3_const_clobber"
+ [(set (match_operand:QI 0 "reg_operand" "=d,c,?d,?c")
+ (lshiftrt:QI (match_operand:QI 1 "src_operand" "0,0,r,r")
+ (match_operand:QI 2 "const_int_operand" "n,n,J,J")))
+ (clobber (reg:CC 21))]
+ "valid_operands (LSHIFTRT, operands, QImode)"
+ "@
+ lsh\\t%n2,%0
+ lsh\\t%n2,%0
+ lsh3\\t%n2,%1,%0
+ lsh3\\t%n2,%1,%0"
+ [(set_attr "type" "binarycc,binarycc,binarycc,binarycc")])
+
+; When the shift count is greater than the size of the word
+; the result can be implementation specific
+(define_insn "*lshrqi3_const_set"
+ [(set (reg:CC 21)
+ (compare:CC
+ (lshiftrt:QI (match_operand:QI 1 "src_operand" "0,r")
+ (match_operand:QI 2 "const_int_operand" "n,J"))
+ (const_int 0)))
+ (set (match_operand:QI 0 "reg_operand" "=?d,d")
+ (lshiftrt:QI (match_dup 1)
+ (match_dup 2)))]
+ "valid_operands (LSHIFTRT, operands, QImode)"
+ "@
+ lsh\\t%n2,%0
+ lsh3\\t%n2,%1,%0"
+ [(set_attr "type" "binarycc,binarycc")])
+
+(define_insn "*lshrqi3_nonconst_clobber"
+ [(set (match_operand:QI 0 "reg_operand" "=d,d,?d,c,c,?c")
+ (lshiftrt:QI (match_operand:QI 1 "src_operand" "0,rR,rS<>,0,rR,rS<>")
+ (neg:QI (match_operand:QI 2 "src_operand" "rm,R,rS<>,rm,R,rS<>"))))
+ (clobber (reg:CC 21))]
+ "valid_operands (LSHIFTRT, operands, QImode)"
+ "@
+ lsh\\t%2,%0
+ lsh3\\t%2,%1,%0
+ lsh3\\t%2,%1,%0
+ lsh\\t%2,%0
+ lsh3\\t%2,%1,%0
+ lsh3\\t%2,%1,%0"
+ [(set_attr "type" "binarycc,binarycc,binarycc,binary,binary,binary")])
+; Default to int16 data attr.
+
+;
+; ASH (right)
+;
+; Arithmetic right shift on the C[34]x works by negating the shift count,
+; then emitting a right shift with the shift count negated. This means
+; that all actual shift counts in the RTL will be positive.
+
+(define_expand "ashrqi3"
+ [(parallel [(set (match_operand:QI 0 "reg_operand" "")
+ (ashiftrt:QI (match_operand:QI 1 "src_operand" "")
+ (match_operand:QI 2 "src_operand" "")))
+ (clobber (reg:CC 21))])]
+ ""
+ "legitimize_operands (ASHIFTRT, operands, QImode);")
+
+; When the shift count is greater than the size of the word
+; the result can be implementation specific
+(define_insn "*ashrqi3_const_clobber"
+ [(set (match_operand:QI 0 "reg_operand" "=d,c,?d,?c")
+ (ashiftrt:QI (match_operand:QI 1 "src_operand" "0,0,r,r")
+ (match_operand:QI 2 "const_int_operand" "n,n,J,J")))
+ (clobber (reg:CC 21))]
+ "valid_operands (ASHIFTRT, operands, QImode)"
+ "@
+ ash\\t%n2,%0
+ ash\\t%n2,%0
+ ash3\\t%n2,%1,%0
+ ash3\\t%n2,%1,%0"
+ [(set_attr "type" "binarycc,binarycc,binarycc,binarycc")])
+
+; When the shift count is greater than the size of the word
+; the result can be implementation specific
+(define_insn "*ashrqi3_const_set"
+ [(set (reg:CC 21)
+ (compare:CC
+ (ashiftrt:QI (match_operand:QI 1 "src_operand" "0,r")
+ (match_operand:QI 2 "const_int_operand" "n,J"))
+ (const_int 0)))
+ (set (match_operand:QI 0 "reg_operand" "=?d,d")
+ (ashiftrt:QI (match_dup 1)
+ (match_dup 2)))]
+ "valid_operands (ASHIFTRT, operands, QImode)"
+ "@
+ ash\\t%n2,%0
+ ash3\\t%n2,%1,%0"
+ [(set_attr "type" "binarycc,binarycc")])
+
+(define_insn "*ashrqi3_nonconst_clobber"
+ [(set (match_operand:QI 0 "reg_operand" "=d,d,?d,c,c,?c")
+ (ashiftrt:QI (match_operand:QI 1 "src_operand" "0,rR,rS<>,0,rR,rS<>")
+ (neg:QI (match_operand:QI 2 "src_operand" "rm,R,rS<>,rm,R,rS<>"))))
+ (clobber (reg:CC 21))]
+ "valid_operands (ASHIFTRT, operands, QImode)"
+ "@
+ ash\\t%2,%0
+ ash3\\t%2,%1,%0
+ ash3\\t%2,%1,%0
+ ash\\t%2,%0
+ ash3\\t%2,%1,%0
+ ash3\\t%2,%1,%0"
+ [(set_attr "type" "binarycc,binarycc,binarycc,binary,binary,binary")])
+; Default to int16 data attr.
+
+;
+; CMPI
+;
+; Unfortunately the C40 doesn't allow cmpi3 7, *ar0++ so the next best
+; thing would be to get the small constant loaded into a register (say r0)
+; so that it could be hoisted out of the loop so that we only
+; would need to do cmpi3 *ar0++, r0. Now the loop optimisation pass
+; comes before the flow pass (which finds autoincrements) so we're stuck.
+; Ideally, GCC requires another loop optimisation pass (preferably after
+; reload) so that it can hoist invariants out of loops.
+; The current solution modifies legitimize_operands () so that small
+; constants are forced into a pseudo register.
+;
+(define_expand "cmpqi"
+ [(set (reg:CC 21)
+ (compare:CC (match_operand:QI 0 "src_operand" "")
+ (match_operand:QI 1 "src_operand" "")))]
+ ""
+ "legitimize_operands (COMPARE, operands, QImode);
+ c4x_compare_op0 = operands[0];
+ c4x_compare_op1 = operands[1];
+ DONE;")
+
+(define_insn "*cmpqi_test"
+ [(set (reg:CC 21)
+ (compare:CC (match_operand:QI 0 "src_operand" "r,rR,rS<>")
+ (match_operand:QI 1 "src_operand" "rIm,JR,rS<>")))]
+ "valid_operands (COMPARE, operands, QImode)"
+ "@
+ cmpi\\t%1,%0
+ cmpi3\\t%1,%0
+ cmpi3\\t%1,%0"
+ [(set_attr "type" "compare,compare,compare")])
+
+(define_insn "*cmpqi_test_noov"
+ [(set (reg:CC_NOOV 21)
+ (compare:CC_NOOV (match_operand:QI 0 "src_operand" "r,rR,rS<>")
+ (match_operand:QI 1 "src_operand" "rIm,JR,rS<>")))]
+ "valid_operands (COMPARE, operands, QImode)"
+ "@
+ cmpi\\t%1,%0
+ cmpi3\\t%1,%0
+ cmpi3\\t%1,%0"
+ [(set_attr "type" "compare,compare,compare")])
+
+(define_expand "udivqi3"
+ [(parallel [(set (match_operand:QI 0 "reg_operand" "")
+ (udiv:QI (match_operand:QI 1 "src_operand" "")
+ (match_operand:QI 2 "src_operand" "")))
+ (clobber (reg:CC 21))])]
+ ""
+ "c4x_emit_libcall3 (UDIVQI3_LIBCALL, UDIV, QImode, operands);
+ DONE;")
+
+(define_expand "divqi3"
+ [(parallel [(set (match_operand:QI 0 "reg_operand" "")
+ (div:QI (match_operand:QI 1 "src_operand" "")
+ (match_operand:QI 2 "src_operand" "")))
+ (clobber (reg:CC 21))])]
+ ""
+ "c4x_emit_libcall3 (DIVQI3_LIBCALL, DIV, QImode, operands);
+ DONE;")
+
+(define_expand "umodqi3"
+ [(parallel [(set (match_operand:QI 0 "reg_operand" "")
+ (umod:QI (match_operand:QI 1 "src_operand" "")
+ (match_operand:QI 2 "src_operand" "")))
+ (clobber (reg:CC 21))])]
+ ""
+ "c4x_emit_libcall3 (UMODQI3_LIBCALL, UMOD, QImode, operands);
+ DONE;")
+
+(define_expand "modqi3"
+ [(parallel [(set (match_operand:QI 0 "reg_operand" "")
+ (mod:QI (match_operand:QI 1 "src_operand" "")
+ (match_operand:QI 2 "src_operand" "")))
+ (clobber (reg:CC 21))])]
+ ""
+ "c4x_emit_libcall3 (MODQI3_LIBCALL, MOD, QImode, operands);
+ DONE;")
+
+(define_expand "ffsqi2"
+ [(parallel [(set (match_operand:QI 0 "reg_operand" "")
+ (ffs:QI (match_operand:QI 1 "src_operand" "")))
+ (clobber (reg:CC 21))])]
+ ""
+ "c4x_emit_libcall (FFS_LIBCALL, FFS, QImode, QImode, 2, operands);
+ DONE;")
+
+;
+; BIT-FIELD INSTRUCTIONS
+;
+
+;
+; LBx/LHw (C4x only)
+;
+(define_expand "extv"
+ [(parallel [(set (match_operand:QI 0 "reg_operand" "")
+ (sign_extract:QI (match_operand:QI 1 "src_operand" "")
+ (match_operand:QI 2 "const_int_operand" "")
+ (match_operand:QI 3 "const_int_operand" "")))
+ (clobber (reg:CC 21))])]
+ "! TARGET_C3X"
+ "if ((INTVAL (operands[2]) != 8 && INTVAL (operands[2]) != 16)
+ || (INTVAL (operands[3]) % INTVAL (operands[2]) != 0))
+ FAIL;
+ ")
+
+(define_insn "*extv_clobber"
+ [(set (match_operand:QI 0 "reg_operand" "=d,c")
+ (sign_extract:QI (match_operand:QI 1 "src_operand" "rLm,rLm")
+ (match_operand:QI 2 "const_int_operand" "n,n")
+ (match_operand:QI 3 "const_int_operand" "n,n")))
+ (clobber (reg:CC 21))]
+ "! TARGET_C3X
+ && (INTVAL (operands[2]) == 8 || INTVAL (operands[2]) == 16)
+ && (INTVAL (operands[3]) % INTVAL (operands[2]) == 0)"
+ "*
+ if (INTVAL (operands[2]) == 8)
+ {
+ operands[3] = GEN_INT (INTVAL (operands[3]) / 8);
+ return \"lb%3\\t%1,%0\";
+ }
+ operands[3] = GEN_INT (INTVAL (operands[3]) / 16);
+ return \"lh%3\\t%1,%0\";
+ "
+ [(set_attr "type" "binarycc,binary")
+ (set_attr "data" "int16,int16")])
+
+(define_insn "*extv_clobber_test"
+ [(set (reg:CC 21)
+ (compare:CC (sign_extract:QI (match_operand:QI 1 "src_operand" "rLm")
+ (match_operand:QI 2 "const_int_operand" "n")
+ (match_operand:QI 3 "const_int_operand" "n"))
+ (const_int 0)))
+ (clobber (match_scratch:QI 0 "=d"))]
+ "! TARGET_C3X
+ && (INTVAL (operands[2]) == 8 || INTVAL (operands[2]) == 16)
+ && (INTVAL (operands[3]) % INTVAL (operands[2]) == 0)"
+ "*
+ if (INTVAL (operands[2]) == 8)
+ {
+ operands[3] = GEN_INT (INTVAL (operands[3]) / 8);
+ return \"lb%3\\t%1,%0\";
+ }
+ operands[3] = GEN_INT (INTVAL (operands[3]) / 16);
+ return \"lh%3\\t%1,%0\";
+ "
+ [(set_attr "type" "binarycc")
+ (set_attr "data" "int16")])
+
+(define_insn "*extv_clobber_set"
+ [(set (reg:CC 21)
+ (compare:CC (sign_extract:QI (match_operand:QI 1 "src_operand" "rLm")
+ (match_operand:QI 2 "const_int_operand" "n")
+ (match_operand:QI 3 "const_int_operand" "n"))
+ (const_int 0)))
+ (set (match_operand:QI 0 "reg_operand" "=d")
+ (sign_extract:QI (match_dup 1)
+ (match_dup 2)
+ (match_dup 3)))]
+ "! TARGET_C3X
+ && (INTVAL (operands[2]) == 8 || INTVAL (operands[2]) == 16)
+ && (INTVAL (operands[3]) % INTVAL (operands[2]) == 0)"
+ "*
+ if (INTVAL (operands[2]) == 8)
+ {
+ operands[3] = GEN_INT (INTVAL (operands[3]) / 8);
+ return \"lb%3\\t%1,%0\";
+ }
+ operands[3] = GEN_INT (INTVAL (operands[3]) / 16);
+ return \"lh%3\\t%1,%0\";
+ "
+ [(set_attr "type" "binarycc")
+ (set_attr "data" "int16")])
+
+;
+; LBUx/LHUw (C4x only)
+;
+(define_expand "extzv"
+ [(parallel [(set (match_operand:QI 0 "reg_operand" "")
+ (zero_extract:QI (match_operand:QI 1 "src_operand" "")
+ (match_operand:QI 2 "const_int_operand" "")
+ (match_operand:QI 3 "const_int_operand" "")))
+ (clobber (reg:CC 21))])]
+ "! TARGET_C3X"
+ "if ((INTVAL (operands[2]) != 8 && INTVAL (operands[2]) != 16)
+ || (INTVAL (operands[3]) % INTVAL (operands[2]) != 0))
+ FAIL;
+ ")
+
+(define_insn "*extzv_clobber"
+ [(set (match_operand:QI 0 "reg_operand" "=d,c")
+ (zero_extract:QI (match_operand:QI 1 "src_operand" "rLm,rLm")
+ (match_operand:QI 2 "const_int_operand" "n,n")
+ (match_operand:QI 3 "const_int_operand" "n,n")))
+ (clobber (reg:CC 21))]
+ "! TARGET_C3X
+ && (INTVAL (operands[2]) == 8 || INTVAL (operands[2]) == 16)
+ && (INTVAL (operands[3]) % INTVAL (operands[2]) == 0)"
+ "*
+ if (INTVAL (operands[2]) == 8)
+ {
+ operands[3] = GEN_INT (INTVAL (operands[3]) / 8);
+ return \"lbu%3\\t%1,%0\";
+ }
+ operands[3] = GEN_INT (INTVAL (operands[3]) / 16);
+ return \"lhu%3\\t%1,%0\";
+ "
+ [(set_attr "type" "binarycc,binary")
+ (set_attr "data" "uint16,uint16")])
+
+(define_insn "*extzv_test"
+ [(set (reg:CC 21)
+ (compare:CC (zero_extract:QI (match_operand:QI 1 "src_operand" "rLm")
+ (match_operand:QI 2 "const_int_operand" "n")
+ (match_operand:QI 3 "const_int_operand" "n"))
+ (const_int 0)))
+ (clobber (match_scratch:QI 0 "=d"))]
+ "! TARGET_C3X
+ && (INTVAL (operands[2]) == 8 || INTVAL (operands[2]) == 16)
+ && (INTVAL (operands[3]) % INTVAL (operands[2]) == 0)"
+ "*
+ if (INTVAL (operands[2]) == 8)
+ {
+ operands[3] = GEN_INT (INTVAL (operands[3]) / 8);
+ return \"lbu%3\\t%1,%0\";
+ }
+ operands[3] = GEN_INT (INTVAL (operands[3]) / 16);
+ return \"lhu%3\\t%1,%0\";
+ "
+ [(set_attr "type" "binarycc")
+ (set_attr "data" "uint16")])
+
+(define_insn "*extzv_set"
+ [(set (reg:CC 21)
+ (compare:CC (zero_extract:QI (match_operand:QI 1 "src_operand" "rLm")
+ (match_operand:QI 2 "const_int_operand" "n")
+ (match_operand:QI 3 "const_int_operand" "n"))
+ (const_int 0)))
+ (set (match_operand:QI 0 "ext_reg_operand" "=d")
+ (zero_extract:QI (match_dup 1)
+ (match_dup 2)
+ (match_dup 3)))]
+ "! TARGET_C3X
+ && (INTVAL (operands[2]) == 8 || INTVAL (operands[2]) == 16)
+ && (INTVAL (operands[3]) % INTVAL (operands[2]) == 0)"
+ "*
+ if (INTVAL (operands[2]) == 8)
+ {
+ /* 8 bit extract. */
+ operands[3] = GEN_INT (INTVAL (operands[3]) / 8);
+ return \"lbu%3\\t%1,%0\";
+ }
+ /* 16 bit extract. */
+ operands[3] = GEN_INT (INTVAL (operands[3]) / 16);
+ return \"lhu%3\\t%1,%0\";
+ "
+ [(set_attr "type" "binarycc")
+ (set_attr "data" "uint16")])
+
+;
+; MBx/MHw (C4x only)
+;
+(define_expand "insv"
+ [(parallel [(set (zero_extract:QI (match_operand:QI 0 "reg_operand" "")
+ (match_operand:QI 1 "const_int_operand" "")
+ (match_operand:QI 2 "const_int_operand" ""))
+ (match_operand:QI 3 "src_operand" ""))
+ (clobber (reg:CC 21))])]
+ "! TARGET_C3X"
+ "if (! (((INTVAL (operands[1]) == 8 || INTVAL (operands[1]) == 16)
+ && (INTVAL (operands[2]) % INTVAL (operands[1]) == 0))
+ || (INTVAL (operands[1]) == 24 && INTVAL (operands[2]) == 8)))
+ FAIL;
+ ")
+
+(define_insn "*insv_clobber"
+ [(set (zero_extract:QI (match_operand:QI 0 "reg_operand" "=d,c")
+ (match_operand:QI 1 "const_int_operand" "n,n")
+ (match_operand:QI 2 "const_int_operand" "n,n"))
+ (match_operand:QI 3 "src_operand" "rLm,rLm"))
+ (clobber (reg:CC 21))]
+ "! TARGET_C3X
+ && (((INTVAL (operands[1]) == 8 || INTVAL (operands[1]) == 16)
+ && (INTVAL (operands[2]) % INTVAL (operands[1]) == 0))
+ || (INTVAL (operands[1]) == 24 && INTVAL (operands[2]) == 8))"
+ "*
+ if (INTVAL (operands[1]) == 8)
+ {
+ /* 8 bit insert. */
+ operands[2] = GEN_INT (INTVAL (operands[2]) / 8);
+ return \"mb%2\\t%3,%0\";
+ }
+ else if (INTVAL (operands[1]) == 16)
+ {
+ /* 16 bit insert. */
+ operands[2] = GEN_INT (INTVAL (operands[2]) / 16);
+ return \"mh%2\\t%3,%0\";
+ }
+ /* 24 bit insert. */
+ return \"lwl1\\t%3,%0\";
+ "
+ [(set_attr "type" "binarycc,binary")
+ (set_attr "data" "uint16,uint16")])
+
+(define_peephole
+ [(parallel [(set (zero_extract:QI (match_operand:QI 0 "ext_reg_operand" "=d")
+ (match_operand:QI 1 "const_int_operand" "n")
+ (match_operand:QI 2 "const_int_operand" "n"))
+ (match_operand:QI 3 "src_operand" "rLm"))
+ (clobber (reg:CC 21))])
+ (set (reg:CC 21)
+ (compare:CC (match_dup 0) (const_int 0)))]
+ "! TARGET_C3X
+ && (INTVAL (operands[1]) == 8 || INTVAL (operands[1]) == 16)
+ && (INTVAL (operands[2]) % INTVAL (operands[1]) == 0)"
+ "*
+ if (INTVAL (operands[1]) == 8)
+ {
+ operands[2] = GEN_INT (INTVAL (operands[2]) / 8);
+ return \"mb%2\\t%3,%0\";
+ }
+ operands[2] = GEN_INT (INTVAL (operands[2]) / 16);
+ return \"mh%2\\t%3,%0\";
+ "
+ [(set_attr "type" "binarycc")
+ (set_attr "data" "uint16")])
+
+
+; TWO OPERAND FLOAT INSTRUCTIONS
+;
+
+;
+; LDF/STF
+;
+; If one of the operands is not a register, then we should
+; emit two insns, using a scratch register. This will produce
+; better code in loops if the source operand is invariant, since
+; the source reload can be optimised out. During reload we cannot
+; use change_address or force_reg.
+(define_expand "movqf"
+ [(set (match_operand:QF 0 "src_operand" "")
+ (match_operand:QF 1 "src_operand" ""))]
+ ""
+ "
+{
+ if (c4x_emit_move_sequence (operands, QFmode))
+ DONE;
+}")
+
+; This can generate invalid stack slot displacements
+(define_split
+ [(set (match_operand:QI 0 "reg_operand" "=r")
+ (unspec [(match_operand:QF 1 "reg_operand" "f")] 12))]
+ "reload_completed"
+ [(set (match_dup 3) (match_dup 1))
+ (set (match_dup 0) (match_dup 2))]
+ "operands[2] = assign_stack_temp (QImode, GET_MODE_SIZE (QImode), 0);
+ operands[3] = copy_rtx (operands[2]);
+ PUT_MODE (operands[3], QFmode);")
+
+
+(define_insn "storeqf_int"
+ [(set (match_operand:QI 0 "reg_operand" "=r")
+ (unspec [(match_operand:QF 1 "reg_operand" "f")] 12))]
+ ""
+ "#"
+ [(set_attr "type" "multi")])
+
+(define_split
+ [(parallel [(set (match_operand:QI 0 "reg_operand" "=r")
+ (unspec [(match_operand:QF 1 "reg_operand" "f")] 12))
+ (clobber (reg:CC 21))])]
+ "reload_completed"
+ [(set (mem:QF (pre_inc:QI (reg:QI 20)))
+ (match_dup 1))
+ (parallel [(set (match_dup 0)
+ (mem:QI (post_dec:QI (reg:QI 20))))
+ (clobber (reg:CC 21))])]
+ "")
+
+
+; We need accurate death notes for this...
+;(define_peephole
+; [(set (match_operand:QF 0 "reg_operand" "=f")
+; (match_operand:QF 1 "memory_operand" "m"))
+; (set (mem:QF (pre_inc:QI (reg:QI 20)))
+; (match_dup 0))
+; (parallel [(set (match_operand:QI 2 "reg_operand" "r")
+; (mem:QI (post_dec:QI (reg:QI 20))))
+; (clobber (reg:CC 21))])]
+; ""
+; "ldiu\\t%1,%0")
+
+(define_insn "storeqf_int_clobber"
+ [(parallel [(set (match_operand:QI 0 "reg_operand" "=r")
+ (unspec [(match_operand:QF 1 "reg_operand" "f")] 12))
+ (clobber (reg:CC 21))])]
+ ""
+ "#"
+ [(set_attr "type" "multi")])
+
+
+; This can generate invalid stack slot displacements
+(define_split
+ [(set (match_operand:QF 0 "reg_operand" "=f")
+ (unspec [(match_operand:QI 1 "reg_operand" "r")] 11))]
+ "reload_completed"
+ [(set (match_dup 2) (match_dup 1))
+ (set (match_dup 0) (match_dup 3))]
+ "operands[2] = assign_stack_temp (QImode, GET_MODE_SIZE (QImode), 0);
+ operands[3] = copy_rtx (operands[2]);
+ PUT_MODE (operands[3], QFmode);")
+
+
+(define_insn "loadqf_int"
+ [(set (match_operand:QF 0 "reg_operand" "=f")
+ (unspec [(match_operand:QI 1 "reg_operand" "r")] 11))]
+ ""
+ "#"
+ [(set_attr "type" "multi")])
+
+(define_split
+ [(parallel [(set (match_operand:QF 0 "reg_operand" "=f")
+ (unspec [(match_operand:QI 1 "reg_operand" "r")] 11))
+ (clobber (reg:CC 21))])]
+ "reload_completed"
+ [(set (mem:QI (pre_inc:QI (reg:QI 20)))
+ (match_dup 1))
+ (parallel [(set (match_dup 0)
+ (mem:QF (post_dec:QI (reg:QI 20))))
+ (clobber (reg:CC 21))])]
+ "")
+
+(define_insn "loadqf_int_clobber"
+ [(parallel [(set (match_operand:QF 0 "reg_operand" "=f")
+ (unspec [(match_operand:QI 1 "reg_operand" "r")] 11))
+ (clobber (reg:CC 21))])]
+ ""
+ "#"
+ [(set_attr "type" "multi")])
+
+; We must provide an alternative to store to memory in case we have to
+; spill a register.
+(define_insn "movqf_noclobber"
+ [(set (match_operand:QF 0 "src_operand" "=f,m")
+ (match_operand:QF 1 "src_operand" "fHm,f"))]
+ "REG_P (operands[0]) || REG_P (operands[1])"
+ "@
+ ldfu\\t%1,%0
+ stf\\t%1,%0"
+ [(set_attr "type" "unary,store")])
+
+;(define_insn "*movqf_clobber"
+; [(set (match_operand:QF 0 "reg_operand" "=f")
+; (match_operand:QF 1 "src_operand" "fHm"))
+; (clobber (reg:CC 21))]
+; "0"
+; "ldf\\t%1,%0"
+; [(set_attr "type" "unarycc")])
+
+(define_insn "*movqf_test"
+ [(set (reg:CC 21)
+ (compare:CC (match_operand:QF 1 "src_operand" "fHm")
+ (const_int 0)))
+ (clobber (match_scratch:QF 0 "=f"))]
+ ""
+ "ldf\\t%1,%0"
+ [(set_attr "type" "unarycc")])
+
+(define_insn "*movqf_set"
+ [(set (reg:CC 21)
+ (compare:CC (match_operand:QF 1 "src_operand" "fHm")
+ (match_operand:QF 2 "fp_zero_operand" "G")))
+ (set (match_operand:QF 0 "reg_operand" "=f")
+ (match_dup 1))]
+ ""
+ "ldf\\t%1,%0"
+ [(set_attr "type" "unarycc")])
+
+(define_insn "*movqf_update"
+ [(set (match_operand:QF 0 "reg_operand" "=r")
+ (mem:QF (plus:QI (match_operand:QI 1 "addr_reg_operand" "a")
+ (match_operand:QI 2 "index_reg_operand" "x"))))
+ (set (match_dup 1)
+ (plus:QI (match_dup 1) (match_dup 2)))]
+ ""
+ "ldfu\\t*%1++(%2),%0"
+ [(set_attr "type" "unary")])
+
+(define_insn "*movqf_parallel"
+ [(set (match_operand:QF 0 "parallel_operand" "=q,S<>,q,S<>")
+ (match_operand:QF 1 "parallel_operand" "S<>,q,S<>,q"))
+ (set (match_operand:QF 2 "parallel_operand" "=q,S<>,S<>,q")
+ (match_operand:QF 3 "parallel_operand" "S<>,q,q,S<>"))]
+ "valid_parallel_load_store (operands, QFmode)"
+ "@
+ ldf1\\t%1,%0\\n||\\tldf2\\t%3,%2
+ stf1\\t%1,%0\\n||\\tstf2\\t%3,%2
+ ldf\\t%1,%0\\n||\\tstf\\t%3,%2
+ ldf\\t%3,%2\\n||\\tstf\\t%1,%0"
+ [(set_attr "type" "load_load,store_store,load_store,store_load")])
+
+
+;
+; PUSH/POP
+;
+(define_insn "*pushqf"
+ [(set (mem:QF (pre_inc:QI (reg:QI 20)))
+ (match_operand:QF 0 "reg_operand" "f"))]
+ ""
+ "pushf\\t%0"
+ [(set_attr "type" "push")])
+
+(define_insn "*popqf"
+ [(set (match_operand:QF 0 "reg_operand" "=f")
+ (mem:QF (post_dec:QI (reg:QI 20))))
+ (clobber (reg:CC 21))]
+ ""
+ "popf\\t%0"
+ [(set_attr "type" "pop")])
+
+
+;
+; ABSF
+;
+(define_expand "absqf2"
+ [(parallel [(set (match_operand:QF 0 "reg_operand" "")
+ (abs:QF (match_operand:QF 1 "src_operand" "")))
+ (clobber (reg:CC_NOOV 21))])]
+""
+"")
+
+(define_insn "*absqf2_clobber"
+ [(set (match_operand:QF 0 "reg_operand" "=f")
+ (abs:QF (match_operand:QF 1 "src_operand" "fHm")))
+ (clobber (reg:CC_NOOV 21))]
+ ""
+ "absf\\t%1,%0"
+ [(set_attr "type" "unarycc")])
+
+(define_insn "*absqf2_test"
+ [(set (reg:CC_NOOV 21)
+ (compare:CC_NOOV (abs:QF (match_operand:QF 1 "src_operand" "fHm"))
+ (match_operand:QF 2 "fp_zero_operand" "G")))
+ (clobber (match_scratch:QF 0 "=f"))]
+ ""
+ "absf\\t%1,%0"
+ [(set_attr "type" "unarycc")])
+
+(define_insn "*absqf2_set"
+ [(set (reg:CC_NOOV 21)
+ (compare:CC_NOOV (abs:QF (match_operand:QF 1 "src_operand" "fHm"))
+ (match_operand:QF 2 "fp_zero_operand" "G")))
+ (set (match_operand:QF 0 "reg_operand" "=f")
+ (abs:QF (match_dup 1)))]
+
+ ""
+ "absf\\t%1,%0"
+ [(set_attr "type" "unarycc")])
+
+;
+; NEGF
+;
+(define_expand "negqf2"
+ [(parallel [(set (match_operand:QF 0 "reg_operand" "")
+ (neg:QF (match_operand:QF 1 "src_operand" "")))
+ (clobber (reg:CC_NOOV 21))])]
+""
+"")
+
+(define_insn "*negqf2_clobber"
+ [(set (match_operand:QF 0 "reg_operand" "=f")
+ (neg:QF (match_operand:QF 1 "src_operand" "fHm")))
+ (clobber (reg:CC_NOOV 21))]
+ ""
+ "negf\\t%1,%0"
+ [(set_attr "type" "unarycc")])
+
+(define_insn "*negqf2_test"
+ [(set (reg:CC_NOOV 21)
+ (compare:CC_NOOV (neg:QF (match_operand:QF 1 "src_operand" "fHm"))
+ (match_operand:QF 2 "fp_zero_operand" "G")))
+ (clobber (match_scratch:QF 0 "=f"))]
+ ""
+ "negf\\t%1,%0"
+ [(set_attr "type" "unarycc")])
+
+(define_insn "*negqf2_set"
+ [(set (reg:CC_NOOV 21)
+ (compare:CC_NOOV (neg:QF (match_operand:QF 1 "src_operand" "fHm"))
+ (match_operand:QF 2 "fp_zero_operand" "G")))
+ (set (match_operand:QF 0 "reg_operand" "=f")
+ (neg:QF (match_dup 1)))]
+ ""
+ "negf\\t%1,%0"
+ [(set_attr "type" "unarycc")])
+
+;
+; FLOAT
+;
+(define_insn "floatqiqf2"
+ [(set (match_operand:QF 0 "reg_operand" "=f")
+ (float:QF (match_operand:QI 1 "src_operand" "rIm")))
+ (clobber (reg:CC 21))]
+ ""
+ "float\\t%1,%0"
+ [(set_attr "type" "unarycc")])
+
+(define_insn "*floatqiqf2_set"
+ [(set (reg:CC 21)
+ (compare:CC (float:QF (match_operand:QI 1 "src_operand" "rIm"))
+ (match_operand:QF 2 "fp_zero_operand" "G")))
+ (set (match_operand:QF 0 "reg_operand" "=f")
+ (float:QF (match_dup 1)))]
+ ""
+ "float\\t%1,%0"
+ [(set_attr "type" "unarycc")])
+
+; Unsigned conversions are a little tricky because we need to
+; add the value for the high bit if necessary.
+;
+;
+(define_expand "floatunsqiqf2"
+ [(set (match_dup 2) (match_dup 3))
+ (parallel [(set (reg:CC 21)
+ (compare:CC (float:QF (match_operand:QI 1 "src_operand" ""))
+ (match_dup 3)))
+ (set (match_dup 4)
+ (float:QF (match_dup 1)))])
+ (set (match_dup 6)
+ (if_then_else:QF (lt (reg:CC 21) (const_int 0))
+ (match_dup 5)
+ (match_dup 2)))
+ (parallel [(set (match_operand:QF 0 "reg_operand" "")
+ (plus:QF (match_dup 6) (match_dup 4)))
+ (clobber (reg:CC_NOOV 21))])]
+ ""
+ "operands[2] = gen_reg_rtx (QFmode);
+ operands[3] = CONST0_RTX (QFmode);
+ operands[4] = gen_reg_rtx (QFmode);
+ operands[5] = gen_reg_rtx (QFmode);
+ operands[6] = gen_reg_rtx (QFmode);
+ emit_move_insn (operands[5],
+ immed_real_const_1 (REAL_VALUE_ATOF (\"4294967296.0\", QFmode), QFmode));")
+
+(define_insn "floatqihf2"
+ [(set (match_operand:HF 0 "reg_operand" "=h")
+ (float:HF (match_operand:QI 1 "src_operand" "rIm")))
+ (clobber (reg:CC 21))]
+ ""
+ "float\\t%1,%0"
+ [(set_attr "type" "unarycc")])
+
+;
+; FIX
+;
+(define_insn "fixqfqi_clobber"
+ [(set (match_operand:QI 0 "reg_operand" "=d,c")
+ (fix:QI (match_operand:QF 1 "src_operand" "fHm,fHm")))
+ (clobber (reg:CC 21))]
+ ""
+ "fix\\t%1,%0"
+ [(set_attr "type" "unarycc")])
+
+(define_insn "*fixqfqi_set"
+ [(set (reg:CC 21)
+ (compare:CC (fix:QI (match_operand:QF 1 "src_operand" "fHm"))
+ (const_int 0)))
+ (set (match_operand:QI 0 "ext_reg_operand" "=d")
+ (fix:QI (match_dup 1)))]
+ ""
+ "fix\\t%1,%0"
+ [(set_attr "type" "unarycc")])
+
+;
+; The C[34]x fix instruction implements a floor, not a straight trunc,
+; so we have to invert the number, fix it, and reinvert it if negative
+;
+(define_expand "fix_truncqfqi2"
+ [(parallel [(set (match_dup 2)
+ (fix:QI (match_operand:QF 1 "src_operand" "")))
+ (clobber (reg:CC 21))])
+ (parallel [(set (match_dup 3) (neg:QF (match_dup 1)))
+ (clobber (reg:CC_NOOV 21))])
+ (parallel [(set (match_dup 4) (fix:QI (match_dup 3)))
+ (clobber (reg:CC 21))])
+ (parallel [(set (reg:CC_NOOV 21)
+ (compare:CC_NOOV (neg:QI (match_dup 4)) (const_int 0)))
+ (set (match_dup 5) (neg:QI (match_dup 4)))])
+ (set (match_dup 2)
+ (if_then_else:QI (le (reg:CC 21) (const_int 0))
+ (match_dup 5)
+ (match_dup 2)))
+ (set (match_operand:QI 0 "reg_operand" "=r") (match_dup 2))]
+ ""
+ "if (TARGET_FAST_FIX)
+ {
+ emit_insn (gen_fixqfqi_clobber (operands[0], operands[1]));
+ DONE;
+ }
+ operands[2] = gen_reg_rtx (QImode);
+ operands[3] = gen_reg_rtx (QFmode);
+ operands[4] = gen_reg_rtx (QImode);
+ operands[5] = gen_reg_rtx (QImode);
+ ")
+
+(define_expand "fix_truncqfhi2"
+ [(parallel [(set (match_operand:HI 0 "reg_operand" "")
+ (fix:HI (match_operand:QF 1 "src_operand" "")))
+ (clobber (reg:CC 21))])]
+ ""
+ "c4x_emit_libcall (FIX_TRUNCQFHI2_LIBCALL, FIX, HImode, QFmode, 2, operands);
+ DONE;")
+
+; Is this allowed to be implementation dependent? If so, we can
+; omit the conditional load. Otherwise we should emit a split.
+(define_expand "fixuns_truncqfqi2"
+ [(parallel [(set (reg:CC 21)
+ (compare:CC (fix:QI (match_operand:QF 1 "src_operand" "fHm"))
+ (const_int 0)))
+ (set (match_dup 2)
+ (fix:QI (match_dup 1)))])
+ (set (match_operand:QI 0 "reg_operand" "=r")
+ (if_then_else:QI (lt (reg:CC 21) (const_int 0))
+ (const_int 0)
+ (match_dup 2)))]
+ ""
+ "operands[2] = gen_reg_rtx (QImode);")
+
+(define_expand "fixuns_truncqfhi2"
+ [(parallel [(set (match_operand:HI 0 "reg_operand" "")
+ (unsigned_fix:HI (match_operand:QF 1 "src_operand" "")))
+ (clobber (reg:CC 21))])]
+ ""
+ "c4x_emit_libcall (FIXUNS_TRUNCQFHI2_LIBCALL, UNSIGNED_FIX,
+ HImode, QFmode, 2, operands);
+ DONE;")
+
+;
+; RCPF
+;
+(define_insn "*rcpfqf_clobber"
+ [(set (match_operand:QF 0 "reg_operand" "=f")
+ (unspec [(match_operand:QF 1 "src_operand" "fHm")] 5))
+ (clobber (reg:CC_NOOV 21))]
+ "! TARGET_C3X"
+ "rcpf\\t%1,%0"
+ [(set_attr "type" "unarycc")])
+
+;
+; RSQRF
+;
+(define_insn "*rsqrfqf_clobber"
+ [(set (match_operand:QF 0 "reg_operand" "=f")
+ (unspec [(match_operand:QF 1 "src_operand" "fHm")] 10))
+ (clobber (reg:CC_NOOV 21))]
+ "! TARGET_C3X"
+ "rsqrf\\t%1,%0"
+ [(set_attr "type" "unarycc")])
+
+;
+; RNDF
+;
+(define_insn "*rndqf_clobber"
+ [(set (match_operand:QF 0 "reg_operand" "=f")
+ (unspec [(match_operand:QF 1 "src_operand" "fHm")] 6))
+ (clobber (reg:CC_NOOV 21))]
+ "! TARGET_C3X"
+ "rnd\\t%1,%0"
+ [(set_attr "type" "unarycc")])
+
+
+; Inlined float square root for C4x
+(define_expand "sqrtqf2_inline"
+ [(parallel [(set (match_dup 2)
+ (unspec [(match_operand:QF 1 "src_operand" "")] 10))
+ (clobber (reg:CC_NOOV 21))])
+ (parallel [(set (match_dup 3) (mult:QF (match_dup 5) (match_dup 1)))
+ (clobber (reg:CC_NOOV 21))])
+ (parallel [(set (match_dup 4) (mult:QF (match_dup 2) (match_dup 3)))
+ (clobber (reg:CC_NOOV 21))])
+ (parallel [(set (match_dup 4) (mult:QF (match_dup 2) (match_dup 4)))
+ (clobber (reg:CC_NOOV 21))])
+ (parallel [(set (match_dup 4) (minus:QF (match_dup 6) (match_dup 4)))
+ (clobber (reg:CC_NOOV 21))])
+ (parallel [(set (match_dup 2) (mult:QF (match_dup 2) (match_dup 4)))
+ (clobber (reg:CC_NOOV 21))])
+ (parallel [(set (match_dup 4) (mult:QF (match_dup 2) (match_dup 3)))
+ (clobber (reg:CC_NOOV 21))])
+ (parallel [(set (match_dup 4) (mult:QF (match_dup 2) (match_dup 4)))
+ (clobber (reg:CC_NOOV 21))])
+ (parallel [(set (match_dup 4) (minus:QF (match_dup 6) (match_dup 4)))
+ (clobber (reg:CC_NOOV 21))])
+ (parallel [(set (match_dup 2) (mult:QF (match_dup 2) (match_dup 4)))
+ (clobber (reg:CC_NOOV 21))])
+ (parallel [(set (match_dup 4) (mult:QF (match_dup 2) (match_dup 1)))
+ (clobber (reg:CC_NOOV 21))])
+ (parallel [(set (match_operand:QF 0 "reg_operand" "")
+ (unspec [(match_dup 4)] 6))
+ (clobber (reg:CC_NOOV 21))])]
+ "! TARGET_C3X"
+ "if (! reload_in_progress
+ && ! reg_operand (operands[1], QFmode))
+ operands[1] = force_reg (QFmode, operands[1]);
+ operands[2] = gen_reg_rtx (QFmode);
+ operands[3] = gen_reg_rtx (QFmode);
+ operands[4] = gen_reg_rtx (QFmode);
+ operands[5] = immed_real_const_1 (REAL_VALUE_ATOF (\"0.5\", QFmode),
+ QFmode);
+ operands[6] = immed_real_const_1 (REAL_VALUE_ATOF (\"1.5\", QFmode),
+ QFmode);")
+
+(define_expand "sqrtqf2"
+ [(parallel [(set (match_operand:QF 0 "reg_operand" "")
+ (sqrt:QF (match_operand:QF 1 "src_operand" "")))
+ (clobber (reg:CC 21))])]
+ ""
+ "if (TARGET_C3X || ! TARGET_INLINE)
+ FAIL;
+ else
+ {
+ emit_insn (gen_sqrtqf2_inline (operands[0], operands[1]));
+ DONE;
+ }
+ ")
+
+;
+; THREE OPERAND FLOAT INSTRUCTIONS
+;
+
+;
+; ADDF
+;
+(define_expand "addqf3"
+ [(parallel [(set (match_operand:QF 0 "reg_operand" "")
+ (plus:QF (match_operand:QF 1 "src_operand" "")
+ (match_operand:QF 2 "src_operand" "")))
+ (clobber (reg:CC_NOOV 21))])]
+ ""
+ "legitimize_operands (PLUS, operands, QFmode);")
+
+(define_insn "*addqf3_clobber"
+ [(set (match_operand:QF 0 "reg_operand" "=f,f,?f")
+ (plus:QF (match_operand:QF 1 "src_operand" "%0,fR,fS<>")
+ (match_operand:QF 2 "src_operand" "fHm,R,fS<>")))
+ (clobber (reg:CC_NOOV 21))]
+ "valid_operands (PLUS, operands, QFmode)"
+ "@
+ addf\\t%2,%0
+ addf3\\t%2,%1,%0
+ addf3\\t%2,%1,%0"
+ [(set_attr "type" "binarycc,binarycc,binarycc")])
+
+(define_insn "*addqf3_test"
+ [(set (reg:CC_NOOV 21)
+ (compare:CC_NOOV (plus:QF (match_operand:QF 1 "src_operand" "%0,fR,fS<>")
+ (match_operand:QF 2 "src_operand" "fHm,R,fS<>"))
+ (match_operand:QF 3 "fp_zero_operand" "G,G,G")))
+ (clobber (match_scratch:QF 0 "=f,f,?f"))]
+ "valid_operands (PLUS, operands, QFmode)"
+ "@
+ addf\\t%2,%0
+ addf3\\t%2,%1,%0
+ addf3\\t%2,%1,%0"
+ [(set_attr "type" "binarycc,binarycc,binarycc")])
+
+(define_insn "*addqf3_set"
+ [(set (reg:CC_NOOV 21)
+ (compare:CC_NOOV (plus:QF (match_operand:QF 1 "src_operand" "%0,fR,fS<>")
+ (match_operand:QF 2 "src_operand" "fHm,R,fS<>"))
+ (match_operand:QF 3 "fp_zero_operand" "G,G,G")))
+ (set (match_operand:QF 0 "reg_operand" "=f,f,?f")
+ (plus:QF (match_dup 1)
+ (match_dup 2)))]
+ "valid_operands (PLUS, operands, QFmode)"
+ "@
+ addf\\t%2,%0
+ addf3\\t%2,%1,%0
+ addf3\\t%2,%1,%0"
+ [(set_attr "type" "binarycc,binarycc,binarycc")])
+
+;
+; SUBF/SUBRF
+;
+(define_expand "subqf3"
+ [(parallel [(set (match_operand:QF 0 "reg_operand" "")
+ (minus:QF (match_operand:QF 1 "src_operand" "")
+ (match_operand:QF 2 "src_operand" "")))
+ (clobber (reg:CC_NOOV 21))])]
+ ""
+ "legitimize_operands (MINUS, operands, QFmode);")
+
+(define_insn "*subqf3_clobber"
+ [(set (match_operand:QF 0 "reg_operand" "=f,f,f,?f")
+ (minus:QF (match_operand:QF 1 "src_operand" "0,fHm,fR,fS<>")
+ (match_operand:QF 2 "src_operand" "fHm,0,R,fS<>")))
+ (clobber (reg:CC_NOOV 21))]
+ "valid_operands (MINUS, operands, QFmode)"
+ "@
+ subf\\t%2,%0
+ subrf\\t%1,%0
+ subf3\\t%2,%1,%0
+ subf3\\t%2,%1,%0"
+ [(set_attr "type" "binarycc,binarycc,binarycc,binarycc")])
+
+(define_insn "*subqf3_test"
+ [(set (reg:CC_NOOV 21)
+ (compare:CC_NOOV (minus:QF (match_operand:QF 1 "src_operand" "0,fHm,fR,fS<>")
+ (match_operand:QF 2 "src_operand" "fHm,0,R,fS<>"))
+ (match_operand:QF 3 "fp_zero_operand" "G,G,G,G")))
+ (clobber (match_scratch:QF 0 "=f,f,f,?f"))]
+ "valid_operands (MINUS, operands, QFmode)"
+ "@
+ subf\\t%2,%0
+ subrf\\t%1,%0
+ subf3\\t%2,%1,%0
+ subf3\\t%2,%1,%0"
+ [(set_attr "type" "binarycc,binarycc,binarycc,binarycc")])
+
+(define_insn "*subqf3_set"
+ [(set (reg:CC_NOOV 21)
+ (compare:CC_NOOV (minus:QF (match_operand:QF 1 "src_operand" "0,fHm,fR,fS<>")
+ (match_operand:QF 2 "src_operand" "0,fHm,R,fS<>"))
+ (match_operand:QF 3 "fp_zero_operand" "G,G,G,G")))
+ (set (match_operand:QF 0 "reg_operand" "=f,f,f,?f")
+ (minus:QF (match_dup 1)
+ (match_dup 2)))]
+ "valid_operands (MINUS, operands, QFmode)"
+ "@
+ subf\\t%2,%0
+ subrf\\t%1,%0
+ subf3\\t%2,%1,%0
+ subf3\\t%2,%1,%0"
+ [(set_attr "type" "binarycc,binarycc,binarycc,binarycc")])
+
+;
+; MPYF
+;
+(define_expand "mulqf3"
+ [(parallel [(set (match_operand:QF 0 "reg_operand" "")
+ (mult:QF (match_operand:QF 1 "src_operand" "")
+ (match_operand:QF 2 "src_operand" "")))
+ (clobber (reg:CC_NOOV 21))])]
+ ""
+ "legitimize_operands (MULT, operands, QFmode);")
+
+(define_insn "*mulqf3_clobber"
+ [(set (match_operand:QF 0 "reg_operand" "=f,f,?f")
+ (mult:QF (match_operand:QF 1 "src_operand" "%0,fR,fS<>")
+ (match_operand:QF 2 "src_operand" "fHm,R,fS<>")))
+ (clobber (reg:CC_NOOV 21))]
+ "valid_operands (MULT, operands, QFmode)"
+ "@
+ mpyf\\t%2,%0
+ mpyf3\\t%2,%1,%0
+ mpyf3\\t%2,%1,%0"
+ [(set_attr "type" "binarycc,binarycc,binarycc")])
+
+(define_insn "*mulqf3_test"
+ [(set (reg:CC_NOOV 21)
+ (compare:CC_NOOV (mult:QF (match_operand:QF 1 "src_operand" "%0,fR,fS<>")
+ (match_operand:QF 2 "src_operand" "fHm,R,fS<>"))
+ (match_operand:QF 3 "fp_zero_operand" "G,G,G")))
+ (clobber (match_scratch:QF 0 "=f,f,?f"))]
+ "valid_operands (MULT, operands, QFmode)"
+ "@
+ mpyf\\t%2,%0
+ mpyf3\\t%2,%1,%0
+ mpyf3\\t%2,%1,%0"
+ [(set_attr "type" "binarycc,binarycc,binarycc")])
+
+(define_insn "*mulqf3_set"
+ [(set (reg:CC_NOOV 21)
+ (compare:CC_NOOV (mult:QF (match_operand:QF 1 "src_operand" "%0,fR,fS<>")
+ (match_operand:QF 2 "src_operand" "fHm,R,fS<>"))
+ (match_operand:QF 3 "fp_zero_operand" "G,G,G")))
+ (set (match_operand:QF 0 "reg_operand" "=f,f,?f")
+ (mult:QF (match_dup 1)
+ (match_dup 2)))]
+ "valid_operands (MULT, operands, QFmode)"
+ "@
+ mpyf\\t%2,%0
+ mpyf3\\t%2,%1,%0
+ mpyf3\\t%2,%1,%0"
+ [(set_attr "type" "binarycc,binarycc,binarycc")])
+
+;
+; CMPF
+;
+(define_expand "cmpqf"
+ [(set (reg:CC 21)
+ (compare:CC (match_operand:QF 0 "src_operand" "")
+ (match_operand:QF 1 "src_operand" "")))]
+ ""
+ "legitimize_operands (COMPARE, operands, QFmode);
+ c4x_compare_op0 = operands[0];
+ c4x_compare_op1 = operands[1];
+ DONE;")
+
+(define_insn "*cmpqf"
+ [(set (reg:CC 21)
+ (compare:CC (match_operand:QF 0 "src_operand" "f,fR,fS<>")
+ (match_operand:QF 1 "src_operand" "fHm,R,fS<>")))]
+ "valid_operands (COMPARE, operands, QFmode)"
+ "@
+ cmpf\\t%1,%0
+ cmpf3\\t%1,%0
+ cmpf3\\t%1,%0"
+ [(set_attr "type" "compare,compare,compare")])
+
+(define_insn "*cmpqf_noov"
+ [(set (reg:CC_NOOV 21)
+ (compare:CC_NOOV (match_operand:QF 0 "src_operand" "f,fR,fS<>")
+ (match_operand:QF 1 "src_operand" "fHm,R,fS<>")))]
+ "valid_operands (COMPARE, operands, QFmode)"
+ "@
+ cmpf\\t%1,%0
+ cmpf3\\t%1,%0
+ cmpf3\\t%1,%0"
+ [(set_attr "type" "compare,compare,compare")])
+
+; Inlined float divide for C4x
+(define_expand "divqf3_inline"
+ [(parallel [(set (match_dup 3)
+ (unspec [(match_operand:QF 2 "src_operand" "")] 5))
+ (clobber (reg:CC_NOOV 21))])
+ (parallel [(set (match_dup 4) (mult:QF (match_dup 2) (match_dup 3)))
+ (clobber (reg:CC_NOOV 21))])
+ (parallel [(set (match_dup 4) (minus:QF (match_dup 5) (match_dup 4)))
+ (clobber (reg:CC_NOOV 21))])
+ (parallel [(set (match_dup 3) (mult:QF (match_dup 3) (match_dup 4)))
+ (clobber (reg:CC_NOOV 21))])
+ (parallel [(set (match_dup 4) (mult:QF (match_dup 2) (match_dup 3)))
+ (clobber (reg:CC_NOOV 21))])
+ (parallel [(set (match_dup 4) (minus:QF (match_dup 5) (match_dup 4)))
+ (clobber (reg:CC_NOOV 21))])
+ (parallel [(set (match_dup 3) (mult:QF (match_dup 3) (match_dup 4)))
+ (clobber (reg:CC_NOOV 21))])
+ (parallel [(set (match_dup 3)
+ (mult:QF (match_operand:QF 1 "src_operand" "")
+ (match_dup 3)))
+ (clobber (reg:CC_NOOV 21))])
+ (parallel [(set (match_operand:QF 0 "reg_operand" "")
+ (unspec [(match_dup 3)] 6))
+ (clobber (reg:CC_NOOV 21))])]
+ "! TARGET_C3X"
+ "if (! reload_in_progress
+ && ! reg_operand (operands[2], QFmode))
+ operands[2] = force_reg (QFmode, operands[2]);
+ operands[3] = gen_reg_rtx (QFmode);
+ operands[4] = gen_reg_rtx (QFmode);
+ operands[5] = CONST2_RTX (QFmode);")
+
+(define_expand "divqf3"
+ [(parallel [(set (match_operand:QF 0 "reg_operand" "")
+ (div:QF (match_operand:QF 1 "src_operand" "")
+ (match_operand:QF 2 "src_operand" "")))
+ (clobber (reg:CC 21))])]
+ ""
+ "if (TARGET_C3X || ! TARGET_INLINE)
+ {
+ c4x_emit_libcall3 (DIVQF3_LIBCALL, DIV, QFmode, operands);
+ DONE;
+ }
+ else
+ {
+ emit_insn (gen_divqf3_inline (operands[0], operands[1], operands[2]));
+ DONE;
+ }
+ ")
+
+;
+; CONDITIONAL MOVES
+;
+
+(define_insn "*ldi_conditional"
+ [(set (match_operand:QI 0 "reg_operand" "=r,r")
+ (if_then_else:QI (match_operator 1 "comparison_operator"
+ [(reg:CC 21) (const_int 0)])
+ (match_operand:QI 2 "src_operand" "rIm,0")
+ (match_operand:QI 3 "src_operand" "0,rIm")))]
+ ""
+ "@
+ ldi%1\\t%2,%0
+ ldi%I1\\t%3,%0"
+ [(set_attr "type" "binary")])
+
+(define_insn "*ldi_conditional_noov"
+ [(set (match_operand:QI 0 "reg_operand" "=r,r")
+ (if_then_else:QI (match_operator 1 "comparison_operator"
+ [(reg:CC_NOOV 21) (const_int 0)])
+ (match_operand:QI 2 "src_operand" "rIm,0")
+ (match_operand:QI 3 "src_operand" "0,rIm")))]
+ "GET_CODE (operands[1]) != LE
+ && GET_CODE (operands[1]) != GE
+ && GET_CODE (operands[1]) != LT
+ && GET_CODE (operands[1]) != GT"
+ "@
+ ldi%1\\t%2,%0
+ ldi%I1\\t%3,%0"
+ [(set_attr "type" "binary")])
+
+; Move operand 2 to operand 0 if condition (operand 1) is true
+; else move operand 3 to operand 0.
+; The temporary register is required below because some of the operands
+; might be identical (namely 0 and 2).
+;
+(define_expand "movqicc"
+ [(set (match_operand:QI 0 "reg_operand" "")
+ (if_then_else:QI (match_operand 1 "comparison_operator" "")
+ (match_operand:QI 2 "src_operand" "")
+ (match_operand:QI 3 "src_operand" "")))]
+ ""
+ "{
+ enum rtx_code code = GET_CODE (operands[1]);
+ rtx ccreg = c4x_gen_compare_reg (code, c4x_compare_op0, c4x_compare_op1);
+ if (ccreg == NULL_RTX) FAIL;
+ emit_insn (gen_rtx_SET (QImode, operands[0],
+ gen_rtx_IF_THEN_ELSE (QImode,
+ gen_rtx (code, VOIDmode, ccreg, const0_rtx),
+ operands[2], operands[3])));
+ DONE;}")
+
+(define_insn "*ldf_conditional"
+ [(set (match_operand:QF 0 "reg_operand" "=f,f")
+ (if_then_else:QF (match_operator 1 "comparison_operator"
+ [(reg:CC 21) (const_int 0)])
+ (match_operand:QF 2 "src_operand" "fHm,0")
+ (match_operand:QF 3 "src_operand" "0,fHm")))]
+ ""
+ "@
+ ldf%1\\t%2,%0
+ ldf%I1\\t%3,%0"
+ [(set_attr "type" "binary")])
+
+(define_insn "*ldf_conditional_noov"
+ [(set (match_operand:QF 0 "reg_operand" "=f,f")
+ (if_then_else:QF (match_operator 1 "comparison_operator"
+ [(reg:CC_NOOV 21) (const_int 0)])
+ (match_operand:QF 2 "src_operand" "fHm,0")
+ (match_operand:QF 3 "src_operand" "0,fHm")))]
+ "GET_CODE (operands[1]) != LE
+ && GET_CODE (operands[1]) != GE
+ && GET_CODE (operands[1]) != LT
+ && GET_CODE (operands[1]) != GT"
+ "@
+ ldf%1\\t%2,%0
+ ldf%I1\\t%3,%0"
+ [(set_attr "type" "binary")])
+
+(define_expand "movqfcc"
+ [(set (match_operand:QF 0 "reg_operand" "")
+ (if_then_else:QF (match_operand 1 "comparison_operator" "")
+ (match_operand:QF 2 "src_operand" "")
+ (match_operand:QF 3 "src_operand" "")))]
+ ""
+ "{
+ enum rtx_code code = GET_CODE (operands[1]);
+ rtx ccreg = c4x_gen_compare_reg (code, c4x_compare_op0, c4x_compare_op1);
+ if (ccreg == NULL_RTX) FAIL;
+ emit_insn (gen_rtx_SET (QFmode, operands[0],
+ gen_rtx_IF_THEN_ELSE (QFmode,
+ gen_rtx (code, VOIDmode, ccreg, const0_rtx),
+ operands[2], operands[3])));
+ DONE;}")
+
+(define_expand "seq"
+ [(set (match_operand:QI 0 "reg_operand" "")
+ (const_int 0))
+ (set (match_dup 0)
+ (if_then_else:QI (eq (match_dup 1) (const_int 0))
+ (const_int 1)
+ (match_dup 0)))]
+ ""
+ "operands[1] = c4x_gen_compare_reg (EQ, c4x_compare_op0, c4x_compare_op1);")
+
+(define_expand "sne"
+ [(set (match_operand:QI 0 "reg_operand" "")
+ (const_int 0))
+ (set (match_dup 0)
+ (if_then_else:QI (ne (match_dup 1) (const_int 0))
+ (const_int 1)
+ (match_dup 0)))]
+ ""
+ "operands[1] = c4x_gen_compare_reg (NE, c4x_compare_op0, c4x_compare_op1);")
+
+(define_expand "slt"
+ [(set (match_operand:QI 0 "reg_operand" "")
+ (const_int 0))
+ (set (match_dup 0)
+ (if_then_else:QI (lt (match_dup 1) (const_int 0))
+ (const_int 1)
+ (match_dup 0)))]
+ ""
+ "operands[1] = c4x_gen_compare_reg (LT, c4x_compare_op0, c4x_compare_op1);
+ if (operands[1] == NULL_RTX) FAIL;")
+
+(define_expand "sltu"
+ [(set (match_operand:QI 0 "reg_operand" "")
+ (const_int 0))
+ (set (match_dup 0)
+ (if_then_else:QI (ltu (match_dup 1) (const_int 0))
+ (const_int 1)
+ (match_dup 0)))]
+ ""
+ "operands[1] = c4x_gen_compare_reg (LTU, c4x_compare_op0, c4x_compare_op1);")
+
+(define_expand "sgt"
+ [(set (match_operand:QI 0 "reg_operand" "")
+ (const_int 0))
+ (set (match_dup 0)
+ (if_then_else:QI (gt (match_dup 1) (const_int 0))
+ (const_int 1)
+ (match_dup 0)))]
+ ""
+ "operands[1] = c4x_gen_compare_reg (GT, c4x_compare_op0, c4x_compare_op1);
+ if (operands[1] == NULL_RTX) FAIL;")
+
+(define_expand "sgtu"
+ [(set (match_operand:QI 0 "reg_operand" "")
+ (const_int 0))
+ (set (match_dup 0)
+ (if_then_else:QI (gtu (match_dup 1) (const_int 0))
+ (const_int 1)
+ (match_dup 0)))]
+ ""
+ "operands[1] = c4x_gen_compare_reg (GTU, c4x_compare_op0, c4x_compare_op1);")
+
+(define_expand "sle"
+ [(set (match_operand:QI 0 "reg_operand" "")
+ (const_int 0))
+ (set (match_dup 0)
+ (if_then_else:QI (le (match_dup 1) (const_int 0))
+ (const_int 1)
+ (match_dup 0)))]
+ ""
+ "operands[1] = c4x_gen_compare_reg (LE, c4x_compare_op0, c4x_compare_op1);
+ if (operands[1] == NULL_RTX) FAIL;")
+
+(define_expand "sleu"
+ [(set (match_operand:QI 0 "reg_operand" "")
+ (const_int 0))
+ (set (match_dup 0)
+ (if_then_else:QI (leu (match_dup 1) (const_int 0))
+ (const_int 1)
+ (match_dup 0)))]
+ ""
+ "operands[1] = c4x_gen_compare_reg (LEU, c4x_compare_op0, c4x_compare_op1);")
+
+(define_expand "sge"
+ [(set (match_operand:QI 0 "reg_operand" "")
+ (const_int 0))
+ (set (match_dup 0)
+ (if_then_else:QI (ge (match_dup 1) (const_int 0))
+ (const_int 1)
+ (match_dup 0)))]
+ ""
+ "operands[1] = c4x_gen_compare_reg (GE, c4x_compare_op0, c4x_compare_op1);
+ if (operands[1] == NULL_RTX) FAIL;")
+
+(define_expand "sgeu"
+ [(set (match_operand:QI 0 "reg_operand" "")
+ (const_int 0))
+ (set (match_dup 0)
+ (if_then_else:QI (geu (match_dup 1) (const_int 0))
+ (const_int 1)
+ (match_dup 0)))]
+ ""
+ "operands[1] = c4x_gen_compare_reg (GEU, c4x_compare_op0, c4x_compare_op1);")
+
+(define_split
+ [(set (match_operand:QI 0 "reg_operand" "")
+ (match_operator 1 "comparison_operator" [(reg:CC 21) (const_int 0)]))]
+ "reload_completed"
+ [(set (match_dup 0) (const_int 0))
+ (set (match_dup 0)
+ (if_then_else:QI (match_op_dup 1 [(reg:CC 21) (const_int 0)])
+ (const_int 1)
+ (match_dup 0)))]
+ "")
+
+(define_split
+ [(set (match_operand:QI 0 "reg_operand" "")
+ (match_operator 1 "comparison_operator" [(reg:CC_NOOV 21) (const_int 0)]))]
+ "reload_completed"
+ [(set (match_dup 0) (const_int 0))
+ (set (match_dup 0)
+ (if_then_else:QI (match_op_dup 1 [(reg:CC_NOOV 21) (const_int 0)])
+ (const_int 1)
+ (match_dup 0)))]
+ "")
+
+(define_insn "*bu"
+ [(set (pc)
+ (unspec [(match_operand:QI 0 "reg_operand" "r")] 1))]
+ ""
+ "bu%#\\t%0"
+ [(set_attr "type" "jump")])
+
+(define_expand "caseqi"
+ [(parallel [(set (match_dup 5)
+ (minus:QI (match_operand:QI 0 "reg_operand" "")
+ (match_operand:QI 1 "src_operand" "")))
+ (clobber (reg:CC_NOOV 21))])
+ (set (reg:CC 21)
+ (compare:CC (match_dup 5)
+ (match_operand:QI 2 "src_operand" "")))
+ (set (pc)
+ (if_then_else (gtu (reg:CC 21)
+ (const_int 0))
+ (label_ref (match_operand 4 "" ""))
+ (pc)))
+ (parallel [(set (match_dup 6)
+ (plus:QI (match_dup 5)
+ (label_ref:QI (match_operand 3 "" ""))))
+ (clobber (reg:CC_NOOV 21))])
+ (set (match_dup 7)
+ (mem:QI (match_dup 6)))
+ (set (pc) (match_dup 7))]
+ ""
+ "operands[5] = gen_reg_rtx (QImode);
+ operands[6] = gen_reg_rtx (QImode);
+ operands[7] = gen_reg_rtx (QImode);")
+
+;
+; PARALLEL FLOAT INSTRUCTIONS
+;
+; This patterns are under development
+
+;
+; ABSF/STF
+;
+
+(define_insn "*absqf2_movqf_clobber"
+ [(set (match_operand:QF 0 "ext_low_reg_operand" "=q")
+ (abs:QF (match_operand:QF 1 "par_ind_operand" "S<>")))
+ (set (match_operand:QF 2 "par_ind_operand" "=S<>")
+ (match_operand:QF 3 "ext_low_reg_operand" "q"))
+ (clobber (reg:CC_NOOV 21))]
+ "TARGET_PARALLEL && valid_parallel_operands_4 (operands, QFmode)"
+ "absf\\t%1,%0\\n||\\tstf\\t%3,%2"
+ [(set_attr "type" "binarycc")])
+
+;
+; ADDF/STF
+;
+
+(define_insn "*addqf3_movqf_clobber"
+ [(set (match_operand:QF 0 "ext_low_reg_operand" "=q")
+ (plus:QF (match_operand:QF 1 "parallel_operand" "%q")
+ (match_operand:QF 2 "parallel_operand" "S<>")))
+ (set (match_operand:QF 3 "par_ind_operand" "=S<>")
+ (match_operand:QF 4 "ext_low_reg_operand" "q"))
+ (clobber (reg:CC 21))]
+ "TARGET_PARALLEL && valid_parallel_operands_5 (operands, QFmode)"
+ "addf3\\t%2,%1,%0\\n||\\tstf\\t%4,%3"
+ [(set_attr "type" "binarycc")])
+
+;
+; FLOAT/STF
+;
+
+(define_insn "*floatqiqf2_movqf_clobber"
+ [(set (match_operand:QF 0 "ext_low_reg_operand" "=q")
+ (float:QF (match_operand:QI 1 "par_ind_operand" "S<>")))
+ (set (match_operand:QF 2 "par_ind_operand" "=S<>")
+ (match_operand:QF 3 "ext_low_reg_operand" "q"))
+ (clobber (reg:CC 21))]
+ "TARGET_PARALLEL && valid_parallel_operands_4 (operands, QFmode)"
+ "float\\t%1,%0\\n||\\tstf\\t%3,%2"
+ [(set_attr "type" "binarycc")])
+
+;
+; MPYF/ADDF
+;
+
+(define_insn "*mulqf3_addqf3_clobber"
+ [(set (match_operand:QF 0 "r0r1_reg_operand" "=t")
+ (mult:QF (match_operand:QF 1 "parallel_operand" "%S<>q")
+ (match_operand:QF 2 "parallel_operand" "S<>q")))
+ (set (match_operand:QF 3 "r2r3_reg_operand" "=u")
+ (plus:QF (match_operand:QF 4 "parallel_operand" "%S<>q")
+ (match_operand:QF 5 "parallel_operand" "S<>q")))
+ (clobber (reg:CC 21))]
+ "TARGET_PARALLEL_MPY && valid_parallel_operands_6 (operands, QFmode)"
+ "mpyf3\\t%2,%1,%0\\n||\\taddf3\\t%5,%4,%3"
+ [(set_attr "type" "binarycc")])
+
+
+;
+; MPYF/STF
+;
+
+(define_insn "*mulqf3_movqf_clobber"
+ [(set (match_operand:QF 0 "ext_low_reg_operand" "=q")
+ (mult:QF (match_operand:QF 1 "parallel_operand" "%q")
+ (match_operand:QF 2 "parallel_operand" "S<>")))
+ (set (match_operand:QF 3 "par_ind_operand" "=S<>")
+ (match_operand:QF 4 "ext_low_reg_operand" "q"))
+ (clobber (reg:CC 21))]
+ "TARGET_PARALLEL && valid_parallel_operands_5 (operands, QFmode)"
+ "mpyf3\\t%2,%1,%0\\n||\\tstf\\t%4,%3"
+ [(set_attr "type" "binarycc")])
+
+;
+; MPYF/SUBF
+;
+
+(define_insn "*mulqf3_subqf3_clobber"
+ [(set (match_operand:QF 0 "r0r1_reg_operand" "=t")
+ (mult:QF (match_operand:QF 1 "parallel_operand" "S<>q")
+ (match_operand:QF 2 "parallel_operand" "S<>q")))
+ (set (match_operand:QF 3 "r2r3_reg_operand" "=u")
+ (minus:QF (match_operand:QF 4 "parallel_operand" "S<>q")
+ (match_operand:QF 5 "parallel_operand" "S<>q")))
+ (clobber (reg:CC 21))]
+ "TARGET_PARALLEL_MPY && valid_parallel_operands_6 (operands, QFmode)"
+ "mpyf3\\t%2,%1,%0\\n||\\tsubf3\\t%5,%4,%3"
+ [(set_attr "type" "binarycc")])
+
+;
+; MPYF/LDF 0
+;
+
+(define_insn "*mulqf3_clrqf_clobber"
+ [(set (match_operand:QF 0 "r0r1_reg_operand" "=t")
+ (mult:QF (match_operand:QF 1 "par_ind_operand" "S<>")
+ (match_operand:QF 2 "par_ind_operand" "S<>")))
+ (set (match_operand:QF 3 "r2r3_reg_operand" "=u")
+ (match_operand:QF 4 "fp_zero_operand" "G"))
+ (clobber (reg:CC 21))]
+ "TARGET_PARALLEL_MPY"
+ "mpyf3\\t%2,%1,%0\\n||\\tsubf3\\t%3,%3,%3"
+ [(set_attr "type" "binarycc")])
+
+;
+; NEGF/STF
+;
+
+(define_insn "*negqf2_movqf_clobber"
+ [(set (match_operand:QF 0 "ext_low_reg_operand" "=q")
+ (neg:QF (match_operand:QF 1 "par_ind_operand" "S<>")))
+ (set (match_operand:QF 2 "par_ind_operand" "=S<>")
+ (match_operand:QF 3 "ext_low_reg_operand" "q"))
+ (clobber (reg:CC 21))]
+ "TARGET_PARALLEL && valid_parallel_operands_4 (operands, QFmode)"
+ "negf\\t%1,%0\\n||\\tstf\\t%3,%2"
+ [(set_attr "type" "binarycc")])
+
+;
+; SUBF/STF
+;
+
+(define_insn "*subqf3_movqf_clobber"
+ [(set (match_operand:QF 0 "ext_low_reg_operand" "=q")
+ (minus:QF (match_operand:QF 1 "ext_low_reg_operand" "q")
+ (match_operand:QF 2 "par_ind_operand" "S<>")))
+ (set (match_operand:QF 3 "par_ind_operand" "=S<>")
+ (match_operand:QF 4 "ext_low_reg_operand" "q"))
+ (clobber (reg:CC 21))]
+ "TARGET_PARALLEL && valid_parallel_operands_5 (operands, QFmode)"
+ "subf3\\t%2,%1,%0\\n||\\tstf\\t%4,%3"
+ [(set_attr "type" "binarycc")])
+
+;
+; PARALLEL INTEGER INSTRUCTIONS
+;
+
+;
+; ABSI/STI
+;
+
+(define_insn "*absqi2_movqi_clobber"
+ [(set (match_operand:QI 0 "ext_low_reg_operand" "=q")
+ (abs:QI (match_operand:QI 1 "par_ind_operand" "S<>")))
+ (set (match_operand:QI 2 "par_ind_operand" "=S<>")
+ (match_operand:QI 3 "ext_low_reg_operand" "q"))
+ (clobber (reg:CC_NOOV 21))]
+ "TARGET_PARALLEL && valid_parallel_operands_4 (operands, QImode)"
+ "absi\\t%1,%0\\n||\\tsti\\t%3,%2"
+ [(set_attr "type" "binarycc")])
+
+;
+; ADDI/STI
+;
+
+(define_insn "*addqi3_movqi_clobber"
+ [(set (match_operand:QI 0 "ext_low_reg_operand" "=q")
+ (plus:QI (match_operand:QI 1 "parallel_operand" "%q")
+ (match_operand:QI 2 "parallel_operand" "S<>")))
+ (set (match_operand:QI 3 "par_ind_operand" "=S<>")
+ (match_operand:QI 4 "ext_low_reg_operand" "q"))
+ (clobber (reg:CC 21))]
+ "TARGET_PARALLEL && valid_parallel_operands_5 (operands, QImode)"
+ "addi3\\t%2,%1,%0\\n||\\tsti\\t%4,%3"
+ [(set_attr "type" "binarycc")])
+
+;
+; AND/STI
+;
+
+(define_insn "*andqi3_movqi_clobber"
+ [(set (match_operand:QI 0 "ext_low_reg_operand" "=q")
+ (and:QI (match_operand:QI 1 "parallel_operand" "%q")
+ (match_operand:QI 2 "parallel_operand" "S<>")))
+ (set (match_operand:QI 3 "par_ind_operand" "=S<>")
+ (match_operand:QI 4 "ext_low_reg_operand" "q"))
+ (clobber (reg:CC 21))]
+ "TARGET_PARALLEL && valid_parallel_operands_5 (operands, QImode)"
+ "and3\\t%2,%1,%0\\n||\\tsti\\t%4,%3"
+ [(set_attr "type" "binarycc")])
+
+;
+; ASH(left)/STI
+;
+
+(define_insn "*ashlqi3_movqi_clobber"
+ [(set (match_operand:QI 0 "ext_low_reg_operand" "=q")
+ (ashift:QI (match_operand:QI 1 "par_ind_operand" "S<>")
+ (match_operand:QI 2 "ext_low_reg_operand" "q")))
+ (set (match_operand:QI 3 "par_ind_operand" "=S<>")
+ (match_operand:QI 4 "ext_low_reg_operand" "q"))
+ (clobber (reg:CC 21))]
+ "TARGET_PARALLEL && valid_parallel_operands_5 (operands, QImode)"
+ "ash3\\t%2,%1,%0\\n||\\tsti\\t%4,%3"
+ [(set_attr "type" "binarycc")])
+
+;
+; ASH(right)/STI
+;
+
+(define_insn "*ashrqi3_movqi_clobber"
+ [(set (match_operand:QI 0 "ext_low_reg_operand" "=q")
+ (ashiftrt:QI (match_operand:QI 1 "par_ind_operand" "S<>")
+ (neg:QI (match_operand:QI 2 "ext_low_reg_operand" "q"))))
+ (set (match_operand:QI 3 "par_ind_operand" "=S<>")
+ (match_operand:QI 4 "ext_low_reg_operand" "q"))
+ (clobber (reg:CC 21))]
+ "TARGET_PARALLEL && valid_parallel_operands_5 (operands, QImode)"
+ "ash3\\t%2,%1,%0\\n||\\tsti\\t%4,%3"
+ [(set_attr "type" "binarycc")])
+
+;
+; FIX/STI
+;
+
+(define_insn "*fixqfqi2_movqi_clobber"
+ [(set (match_operand:QI 0 "ext_low_reg_operand" "=q")
+ (fix:QI (match_operand:QF 1 "par_ind_operand" "S<>")))
+ (set (match_operand:QI 2 "par_ind_operand" "=S<>")
+ (match_operand:QI 3 "ext_low_reg_operand" "q"))
+ (clobber (reg:CC 21))]
+ "TARGET_PARALLEL && valid_parallel_operands_4 (operands, QImode)"
+ "fix\\t%1,%0\\n||\\tsti\\t%3,%2"
+ [(set_attr "type" "binarycc")])
+
+;
+; LSH(right)/STI
+;
+
+(define_insn "*lshrqi3_movqi_clobber"
+ [(set (match_operand:QI 0 "ext_low_reg_operand" "=q")
+ (lshiftrt:QI (match_operand:QI 1 "par_ind_operand" "S<>")
+ (neg:QI (match_operand:QI 2 "ext_low_reg_operand" "q"))))
+ (set (match_operand:QI 3 "par_ind_operand" "=S<>")
+ (match_operand:QI 4 "ext_low_reg_operand" "q"))
+ (clobber (reg:CC 21))]
+ "TARGET_PARALLEL && valid_parallel_operands_5 (operands, QImode)"
+ "lsh3\\t%2,%1,%0\\n||\\tsti\\t%4,%3"
+ [(set_attr "type" "binarycc")])
+
+;
+; MPYI/ADDI
+;
+
+(define_insn "*mulqi3_addqi3_clobber"
+ [(set (match_operand:QI 0 "r0r1_reg_operand" "=t")
+ (mult:QI (match_operand:QI 1 "parallel_operand" "S<>q")
+ (match_operand:QI 2 "parallel_operand" "S<>q")))
+ (set (match_operand:QI 3 "r2r3_reg_operand" "=u")
+ (plus:QI (match_operand:QI 4 "parallel_operand" "S<>q")
+ (match_operand:QI 5 "parallel_operand" "S<>q")))
+ (clobber (reg:CC 21))]
+ "TARGET_PARALLEL_MPY && TARGET_MPYI
+ && valid_parallel_operands_6 (operands, QImode)"
+ "mpyi3\\t%2,%1,%0\\n||\\taddi3\\t%5,%4,%3"
+ [(set_attr "type" "binarycc")])
+
+
+;
+; MPYI/STI
+;
+
+(define_insn "*mulqi3_movqi_clobber"
+ [(set (match_operand:QI 0 "ext_low_reg_operand" "=q")
+ (mult:QI (match_operand:QI 1 "parallel_operand" "%q")
+ (match_operand:QI 2 "parallel_operand" "S<>")))
+ (set (match_operand:QI 3 "par_ind_operand" "=S<>")
+ (match_operand:QI 4 "ext_low_reg_operand" "q"))
+ (clobber (reg:CC 21))]
+ "TARGET_PARALLEL && TARGET_MPYI
+ && valid_parallel_operands_5 (operands, QImode)"
+ "mpyi3\\t%2,%1,%0\\n||\\tsti\\t%4,%3"
+ [(set_attr "type" "binarycc")])
+
+;
+; MPYI/SUBI
+;
+
+(define_insn "*mulqi3_subqi3_clobber"
+ [(set (match_operand:QI 0 "r0r1_reg_operand" "=t")
+ (mult:QI (match_operand:QI 1 "parallel_operand" "S<>q")
+ (match_operand:QI 2 "parallel_operand" "S<>q")))
+ (set (match_operand:QI 3 "r2r3_reg_operand" "=u")
+ (minus:QI (match_operand:QI 4 "parallel_operand" "S<>q")
+ (match_operand:QI 5 "parallel_operand" "S<>q")))
+ (clobber (reg:CC 21))]
+ "TARGET_PARALLEL_MPY && TARGET_MPYI
+ && valid_parallel_operands_6 (operands, QImode)"
+ "mpyi3\\t%2,%1,%0\\n||\\tsubi3\\t%5,%4,%3"
+ [(set_attr "type" "binarycc")])
+
+;
+; MPYI/LDI 0
+;
+
+(define_insn "*mulqi3_clrqi_clobber"
+ [(set (match_operand:QI 0 "r0r1_reg_operand" "=t")
+ (mult:QI (match_operand:QI 1 "par_ind_operand" "S<>")
+ (match_operand:QI 2 "par_ind_operand" "S<>")))
+ (set (match_operand:QI 3 "r2r3_reg_operand" "=u")
+ (const_int 0))
+ (clobber (reg:CC 21))]
+ "TARGET_PARALLEL_MPY && TARGET_MPYI"
+ "mpyi3\\t%2,%1,%0\\n||\\tsubi3\\t%3,%3,%3"
+ [(set_attr "type" "binarycc")])
+
+;
+; NEGI/STI
+;
+
+(define_insn "*negqi2_movqi_clobber"
+ [(set (match_operand:QI 0 "ext_low_reg_operand" "=q")
+ (neg:QI (match_operand:QI 1 "par_ind_operand" "S<>")))
+ (set (match_operand:QI 2 "par_ind_operand" "=S<>")
+ (match_operand:QI 3 "ext_low_reg_operand" "q"))
+ (clobber (reg:CC 21))]
+ "TARGET_PARALLEL && valid_parallel_operands_4 (operands, QImode)"
+ "negi\\t%1,%0\\n||\\tsti\\t%3,%2"
+ [(set_attr "type" "binarycc")])
+
+;
+; NOT/STI
+;
+
+(define_insn "*notqi2_movqi_clobber"
+ [(set (match_operand:QI 0 "ext_low_reg_operand" "=q")
+ (not:QI (match_operand:QI 1 "par_ind_operand" "S<>")))
+ (set (match_operand:QI 2 "par_ind_operand" "=S<>")
+ (match_operand:QI 3 "ext_low_reg_operand" "q"))
+ (clobber (reg:CC 21))]
+ "TARGET_PARALLEL && valid_parallel_operands_4 (operands, QImode)"
+ "not\\t%1,%0\\n||\\tsti\\t%3,%2"
+ [(set_attr "type" "binarycc")])
+
+;
+; OR/STI
+;
+
+(define_insn "*iorqi3_movqi_clobber"
+ [(set (match_operand:QI 0 "ext_low_reg_operand" "=q")
+ (ior:QI (match_operand:QI 1 "parallel_operand" "%q")
+ (match_operand:QI 2 "parallel_operand" "S<>")))
+ (set (match_operand:QI 3 "par_ind_operand" "=S<>")
+ (match_operand:QI 4 "ext_low_reg_operand" "q"))
+ (clobber (reg:CC 21))]
+ "TARGET_PARALLEL && valid_parallel_operands_5 (operands, QImode)"
+ "or3\\t%2,%1,%0\\n||\\tsti\\t%4,%3"
+ [(set_attr "type" "binarycc")])
+
+;
+; SUBI/STI
+;
+
+(define_insn "*subqi3_movqi_clobber"
+ [(set (match_operand:QI 0 "ext_low_reg_operand" "=q")
+ (minus:QI (match_operand:QI 1 "ext_low_reg_operand" "q")
+ (match_operand:QI 2 "par_ind_operand" "S<>")))
+ (set (match_operand:QI 3 "par_ind_operand" "=S<>")
+ (match_operand:QI 4 "ext_low_reg_operand" "q"))
+ (clobber (reg:CC 21))]
+ "TARGET_PARALLEL && valid_parallel_operands_5 (operands, QImode)"
+ "subi3\\t%2,%1,%0\\n||\\tsti\\t%4,%3"
+ [(set_attr "type" "binarycc")])
+
+;
+; XOR/STI
+;
+
+(define_insn "*xorqi3_movqi_clobber"
+ [(set (match_operand:QI 0 "ext_low_reg_operand" "=q")
+ (xor:QI (match_operand:QI 1 "parallel_operand" "%q")
+ (match_operand:QI 2 "parallel_operand" "S<>")))
+ (set (match_operand:QI 3 "par_ind_operand" "=S<>")
+ (match_operand:QI 4 "ext_low_reg_operand" "q"))
+ (clobber (reg:CC 21))]
+ "TARGET_PARALLEL && valid_parallel_operands_5 (operands, QImode)"
+ "xor3\\t%2,%1,%0\\n||\\tsti\\t%4,%3"
+ [(set_attr "type" "binarycc")])
+
+;
+; BRANCH/CALL INSTRUCTIONS
+;
+
+;
+; Branch instructions
+;
+(define_insn "*b"
+ [(set (pc) (if_then_else (match_operator 0 "comparison_operator"
+ [(reg:CC 21) (const_int 0)])
+ (label_ref (match_operand 1 "" ""))
+ (pc)))]
+ ""
+ "*
+ return c4x_output_cbranch (\"b%0\", insn);"
+ [(set_attr "type" "jmpc")])
+
+(define_insn "*b_rev"
+ [(set (pc) (if_then_else (match_operator 0 "comparison_operator"
+ [(reg:CC 21) (const_int 0)])
+ (pc)
+ (label_ref (match_operand 1 "" ""))))]
+ ""
+ "*
+ return c4x_output_cbranch (\"b%I0\", insn);"
+ [(set_attr "type" "jmpc")])
+
+(define_insn "*b_noov"
+ [(set (pc) (if_then_else (match_operator 0 "comparison_operator"
+ [(reg:CC_NOOV 21) (const_int 0)])
+ (label_ref (match_operand 1 "" ""))
+ (pc)))]
+ "GET_CODE (operands[0]) != LE
+ && GET_CODE (operands[0]) != GE
+ && GET_CODE (operands[0]) != LT
+ && GET_CODE (operands[0]) != GT"
+ "*
+ return c4x_output_cbranch (\"b%0\", insn);"
+ [(set_attr "type" "jmpc")])
+
+(define_insn "*b_noov_rev"
+ [(set (pc) (if_then_else (match_operator 0 "comparison_operator"
+ [(reg:CC_NOOV 21) (const_int 0)])
+ (pc)
+ (label_ref (match_operand 1 "" ""))))]
+ "GET_CODE (operands[0]) != LE
+ && GET_CODE (operands[0]) != GE
+ && GET_CODE (operands[0]) != LT
+ && GET_CODE (operands[0]) != GT"
+ "*
+ return c4x_output_cbranch (\"b%I0\", insn);"
+ [(set_attr "type" "jmpc")])
+
+(define_expand "beq"
+ [(set (pc) (if_then_else (eq (match_dup 1) (const_int 0))
+ (label_ref (match_operand 0 "" ""))
+ (pc)))]
+ ""
+ "operands[1] = c4x_gen_compare_reg (EQ, c4x_compare_op0, c4x_compare_op1);")
+
+(define_expand "bne"
+ [(set (pc) (if_then_else (ne (match_dup 1) (const_int 0))
+ (label_ref (match_operand 0 "" ""))
+ (pc)))]
+ ""
+ "operands[1] = c4x_gen_compare_reg (NE, c4x_compare_op0, c4x_compare_op1);")
+
+(define_expand "blt"
+ [(set (pc) (if_then_else (lt (match_dup 1) (const_int 0))
+ (label_ref (match_operand 0 "" ""))
+ (pc)))]
+ ""
+ "operands[1] = c4x_gen_compare_reg (LT, c4x_compare_op0, c4x_compare_op1);
+ if (operands[1] == NULL_RTX) FAIL;")
+
+(define_expand "bltu"
+ [(set (pc) (if_then_else (ltu (match_dup 1) (const_int 0))
+ (label_ref (match_operand 0 "" ""))
+ (pc)))]
+ ""
+ "operands[1] = c4x_gen_compare_reg (LTU, c4x_compare_op0, c4x_compare_op1);")
+
+(define_expand "bgt"
+ [(set (pc) (if_then_else (gt (match_dup 1) (const_int 0))
+ (label_ref (match_operand 0 "" ""))
+ (pc)))]
+ ""
+ "operands[1] = c4x_gen_compare_reg (GT, c4x_compare_op0, c4x_compare_op1);
+ if (operands[1] == NULL_RTX) FAIL;")
+
+(define_expand "bgtu"
+ [(set (pc) (if_then_else (gtu (match_dup 1) (const_int 0))
+ (label_ref (match_operand 0 "" ""))
+ (pc)))]
+ ""
+ "operands[1] = c4x_gen_compare_reg (GTU, c4x_compare_op0, c4x_compare_op1);")
+
+(define_expand "ble"
+ [(set (pc) (if_then_else (le (match_dup 1) (const_int 0))
+ (label_ref (match_operand 0 "" ""))
+ (pc)))]
+ ""
+ "operands[1] = c4x_gen_compare_reg (LE, c4x_compare_op0, c4x_compare_op1);
+ if (operands[1] == NULL_RTX) FAIL;")
+
+(define_expand "bleu"
+ [(set (pc) (if_then_else (leu (match_dup 1) (const_int 0))
+ (label_ref (match_operand 0 "" ""))
+ (pc)))]
+ ""
+ "operands[1] = c4x_gen_compare_reg (LEU, c4x_compare_op0, c4x_compare_op1);")
+
+(define_expand "bge"
+ [(set (pc) (if_then_else (ge (match_dup 1) (const_int 0))
+ (label_ref (match_operand 0 "" ""))
+ (pc)))]
+ ""
+ "operands[1] = c4x_gen_compare_reg (GE, c4x_compare_op0, c4x_compare_op1);
+ if (operands[1] == NULL_RTX) FAIL;")
+
+(define_expand "bgeu"
+ [(set (pc) (if_then_else (geu (match_dup 1) (const_int 0))
+ (label_ref (match_operand 0 "" ""))
+ (pc)))]
+ ""
+ "operands[1] = c4x_gen_compare_reg (GEU, c4x_compare_op0, c4x_compare_op1);")
+
+(define_insn "*b_reg"
+ [(set (pc) (match_operand:QI 0 "reg_operand" "r"))]
+ ""
+ "bu%#\\t%0"
+ [(set_attr "type" "jump")])
+
+(define_expand "indirect_jump"
+ [(set (pc) (match_operand:QI 0 "reg_operand" ""))]
+ ""
+ "")
+
+(define_insn "tablejump"
+ [(set (pc) (match_operand:QI 0 "src_operand" "r"))
+ (use (label_ref (match_operand 1 "" "")))]
+ ""
+ "bu%#\\t%0"
+ [(set_attr "type" "jump")])
+
+;
+; CALL
+;
+(define_insn "*call_c3x"
+ [(call (mem:QI (match_operand:QI 0 "call_address_operand" "Ur"))
+ (match_operand:QI 1 "general_operand" ""))
+ (clobber (reg:QI 31))]
+ ;; Operand 1 not really used on the C4x. The C30 doesn't have reg 31.
+
+ "TARGET_C3X"
+ "call%U0\\t%C0"
+ [(set_attr "type" "call")])
+
+; LAJ requires R11 (31) for the return address
+(define_insn "*laj"
+ [(call (mem:QI (match_operand:QI 0 "call_address_operand" "Ur"))
+ (match_operand:QI 1 "general_operand" ""))
+ (clobber (reg:QI 31))]
+ ;; Operand 1 not really used on the C4x.
+
+ "! TARGET_C3X"
+ "*
+ if (final_sequence)
+ return \"laj%U0\\t%C0\";
+ else
+ return \"call%U0\\t%C0\";"
+ [(set_attr "type" "laj")])
+
+(define_expand "call"
+ [(parallel [(call (match_operand:QI 0 "" "")
+ (match_operand:QI 1 "general_operand" ""))
+ (clobber (reg:QI 31))])]
+ ""
+ "
+{
+ if (GET_CODE (operands[0]) == MEM
+ && ! call_address_operand (XEXP (operands[0], 0), Pmode))
+ operands[0] = gen_rtx_MEM (GET_MODE (operands[0]),
+ force_reg (Pmode, XEXP (operands[0], 0)));
+}")
+
+(define_insn "*callv_c3x"
+ [(set (match_operand 0 "" "=r")
+ (call (mem:QI (match_operand:QI 1 "call_address_operand" "Ur"))
+ (match_operand:QI 2 "general_operand" "")))
+ (clobber (reg:QI 31))]
+ ;; Operand 0 and 2 not really used for the C4x.
+ ;; The C30 doesn't have reg 31.
+
+ "TARGET_C3X"
+ "call%U1\\t%C1"
+ [(set_attr "type" "call")])
+
+; LAJ requires R11 (31) for the return address
+(define_insn "*lajv"
+ [(set (match_operand 0 "" "=r")
+ (call (mem:QI (match_operand:QI 1 "call_address_operand" "Ur"))
+ (match_operand:QI 2 "general_operand" "")))
+ (clobber (reg:QI 31))]
+ ;; Operand 0 and 2 not really used in the C30 instruction.
+
+ "! TARGET_C3X"
+ "*
+ if (final_sequence)
+ return \"laj%U1\\t%C1\";
+ else
+ return \"call%U1\\t%C1\";"
+ [(set_attr "type" "laj")])
+
+(define_expand "call_value"
+ [(parallel [(set (match_operand 0 "" "")
+ (call (match_operand:QI 1 "" "")
+ (match_operand:QI 2 "general_operand" "")))
+ (clobber (reg:QI 31))])]
+ ""
+ "
+{
+ if (GET_CODE (operands[0]) == MEM
+ && ! call_address_operand (XEXP (operands[1], 0), Pmode))
+ operands[0] = gen_rtx_MEM (GET_MODE (operands[1]),
+ force_reg (Pmode, XEXP (operands[1], 0)));
+}")
+
+(define_insn "return"
+ [(return)]
+ "c4x_null_epilogue_p ()"
+ "rets"
+ [(set_attr "type" "rets")])
+
+(define_insn "*return_cc"
+ [(set (pc)
+ (if_then_else (match_operator 0 "comparison_operator"
+ [(reg:CC 21) (const_int 0)])
+ (return)
+ (pc)))]
+ "c4x_null_epilogue_p ()"
+ "rets%0"
+ [(set_attr "type" "rets")])
+
+(define_insn "*return_cc_noov"
+ [(set (pc)
+ (if_then_else (match_operator 0 "comparison_operator"
+ [(reg:CC_NOOV 21) (const_int 0)])
+ (return)
+ (pc)))]
+ "GET_CODE (operands[0]) != LE
+ && GET_CODE (operands[0]) != GE
+ && GET_CODE (operands[0]) != LT
+ && GET_CODE (operands[0]) != GT
+ && c4x_null_epilogue_p ()"
+ "rets%0"
+ [(set_attr "type" "rets")])
+
+(define_insn "*return_cc_inverse"
+ [(set (pc)
+ (if_then_else (match_operator 0 "comparison_operator"
+ [(reg:CC 21) (const_int 0)])
+ (pc)
+ (return)))]
+ "c4x_null_epilogue_p ()"
+ "rets%I0"
+ [(set_attr "type" "rets")])
+
+(define_insn "*return_cc_noov_inverse"
+ [(set (pc)
+ (if_then_else (match_operator 0 "comparison_operator"
+ [(reg:CC_NOOV 21) (const_int 0)])
+ (pc)
+ (return)))]
+ "GET_CODE (operands[0]) != LE
+ && GET_CODE (operands[0]) != GE
+ && GET_CODE (operands[0]) != LT
+ && GET_CODE (operands[0]) != GT
+ && c4x_null_epilogue_p ()"
+ "rets%I0"
+ [(set_attr "type" "rets")])
+
+(define_insn "jump"
+ [(set (pc) (label_ref (match_operand 0 "" "")))]
+ ""
+ "br%#\\t%l0"
+ [(set_attr "type" "jump")])
+
+;
+; DBcond
+;
+; Note we have to emit a dbu instruction if there are no delay slots
+; to fill.
+; Also note that GCC will try to reverse a loop to see if it can
+; utilise this instruction. However, if there are more than one
+; memory reference in the loop, it cannot guarantee that reversing
+; the loop will work :( (see check_dbra_loop() in loop.c)
+; Note that the C3x only decrements the 24 LSBs of the address register
+; and the 8 MSBs are untouched. The C4x uses all 32-bits. We thus
+; have an option to disable this instruction.
+(define_insn "*db"
+ [(set (pc)
+ (if_then_else (ne (match_operand:QI 0 "addr_reg_operand" "+a,?*d,??*r,!m")
+ (const_int 0))
+ (label_ref (match_operand 1 "" ""))
+ (pc)))
+ (set (match_dup 0)
+ (plus:QI (match_dup 0)
+ (const_int -1)))
+ (clobber (reg:CC_NOOV 21))]
+ "TARGET_DB && TARGET_LOOP_UNSIGNED"
+ "*
+ if (which_alternative == 0)
+ return \"dbu%#\\t%0,%l1\";
+ else if (which_alternative == 1)
+ return c4x_output_cbranch (\"subi\\t1,%0\\n\\tbge\", insn);
+ else if (which_alternative == 2)
+ return c4x_output_cbranch (\"subi\\t1,%0\\n\\tcmpi\\t0,%0\\n\\tbge\", insn);
+ else
+ return c4x_output_cbranch (\"push\\tr0\\n\\tldi\\t%0,r0\\n\\tsubi\\t1,r0\\n\\tsti\\tr0,%0\\n\\tpop\\tr0\\n\\tbhs\", insn);
+ "
+ [(set_attr "type" "db,jmpc,jmpc,jmpc")])
+
+
+; This insn is used for some loop tests, typically loops reversed when
+; strength reduction is used. It is actually created when the instruction
+; combination phase combines the special loop test. Since this insn
+; is both a jump insn and has an output, it must deal with its own
+; reloads, hence the `m' constraints.
+
+; The C4x does the decrement and then compares the result against zero.
+; It branches if the result was greater than or equal to zero.
+; In the RTL the comparison and decrement are assumed to happen
+; at the same time so we bias the iteration counter with by -1
+; when we make the test.
+(define_insn "decrement_and_branch_until_zero"
+ [(set (pc)
+ (if_then_else (ge (plus:QI (match_operand:QI 0 "addr_reg_operand" "+a,?*d,??*r,!m")
+ (const_int -1))
+ (const_int 0))
+ (label_ref (match_operand 1 "" ""))
+ (pc)))
+ (set (match_dup 0)
+ (plus:QI (match_dup 0)
+ (const_int -1)))
+ (clobber (reg:CC_NOOV 21))]
+ "TARGET_DB && (find_reg_note (insn, REG_NONNEG, 0) || TARGET_LOOP_UNSIGNED)"
+ "*
+ if (which_alternative == 0)
+ return \"dbu%#\\t%0,%l1\";
+ else if (which_alternative == 1)
+ return c4x_output_cbranch (\"subi\\t1,%0\\n\\tbge\", insn);
+ else if (which_alternative == 2)
+ return c4x_output_cbranch (\"subi\\t1,%0\\n\\tcmpi\\t0,%0\\n\\tbge\", insn);
+ else
+ return c4x_output_cbranch (\"push\\tr0\\n\\tldi\\t%0,r0\\n\\tsubi\\t1,r0\\n\\tsti\\tr0,%0\\n\\tpop\\tr0\\n\\tbhs\", insn);
+ "
+ [(set_attr "type" "db,jmpc,jmpc,jmpc")])
+
+;
+; MISC INSTRUCTIONS
+;
+
+;
+; NOP
+;
+(define_insn "nop"
+ [(const_int 0)]
+ ""
+ "nop")
+; Default to misc type attr.
+
+
+;
+; RPTB
+;
+(define_insn "rptb_top"
+ [(use (label_ref (match_operand 0 "" "")))
+ (use (label_ref (match_operand 1 "" "")))]
+ ""
+ "*
+ return ! final_sequence && c4x_rptb_rpts_p (insn, operands[0])
+ ? \"rpts\\trc\" : \"rptb%#\\t%l1-1\";
+ "
+ [(set_attr "type" "repeat_top")])
+
+; This pattern needs to be emitted at the start of the loop to
+; say that RS and RE are loaded.
+(define_insn "*rptb_init"
+ [(unspec[(match_operand:QI 0 "register_operand" "va")] 22)
+ (use (match_operand:QI 1 "const_int_operand" ""))
+ (use (match_operand:QI 2 "const_int_operand" ""))
+ (use (match_operand:QI 3 "const_int_operand" ""))
+ (clobber (reg:QI 25))
+ (clobber (reg:QI 26))]
+ ""
+ ""
+ [(set_attr "type" "repeat")])
+
+
+(define_expand "doloop_begin"
+ [(parallel [(unspec[(match_operand:QI 0 "register_operand" "va")] 22)
+ (use (match_operand:QI 1 "const_int_operand" ""))
+ (use (match_operand:QI 2 "const_int_operand" ""))
+ (use (match_operand:QI 3 "const_int_operand" ""))
+ (clobber (reg:QI 25))
+ (clobber (reg:QI 26))])]
+ ""
+ "if (INTVAL (operands[3]) > 1 || ! TARGET_RPTB)
+ FAIL;")
+
+
+; The RS (25) and RE (26) registers must be unviolate from the top of the loop
+; to here.
+(define_insn "rptb_end"
+ [(set (pc)
+ (if_then_else (ge (match_operand:QI 0 "register_operand" "+v,?a,!*d,!*x*k,!m")
+ (const_int 0))
+ (label_ref (match_operand 1 "" ""))
+ (pc)))
+ (set (match_dup 0)
+ (plus:QI (match_dup 0)
+ (const_int -1)))
+ (use (match_operand:QI 2 "const_int_operand" ""))
+ (use (match_operand:QI 3 "const_int_operand" ""))
+ (use (match_operand:QI 4 "const_int_operand" ""))
+ (use (reg:QI 25))
+ (use (reg:QI 26))
+ (clobber (reg:CC_NOOV 21))]
+ ""
+ "*
+ if (which_alternative == 0)
+ return c4x_rptb_nop_p (insn) ? \"nop\" : \"\";
+ else if (which_alternative == 1 && TARGET_DB)
+ return \"dbu%#\\t%0,%l1\";
+ else if (which_alternative == 2)
+ return c4x_output_cbranch (\"subi\\t1,%0\\n\\tbge\", insn);
+ else if (which_alternative == 3 || (which_alternative == 1 && ! TARGET_DB))
+ return c4x_output_cbranch (\"subi\\t1,%0\\n\\tcmpi\\t0,%0\\n\\tbge\", insn);
+ else
+ return c4x_output_cbranch (\"push\\tr0\\n\\tldi\\t%0,r0\\n\\tsubi\\t1,r0\\n\\tsti\\tr0,%0\\n\\tpop\\tr0\\n\\tbhs\", insn);
+ "
+ [(set_attr "type" "repeat,db,jmpc,jmpc,jmpc")])
+
+; operand 0 is the loop count register
+; operand 1 is the label to jump to at the top of the loop
+; operand 2 is the number of loop iterations or 0 if it is unknown
+; operand 3 is the maximum number of loop iterations
+; operand 4 is the number of levels of enclosed loops
+(define_expand "doloop_end"
+ [(parallel [(set (pc)
+ (if_then_else (ge (match_operand:QI 0 "register_operand" "")
+ (const_int 0))
+ (label_ref (match_operand 1 "" ""))
+ (pc)))
+ (set (match_dup 0)
+ (plus:QI (match_dup 0)
+ (const_int -1)))
+ (use (match_operand:QI 2 "const_int_operand" ""))
+ (use (match_operand:QI 3 "const_int_operand" ""))
+ (use (match_operand:QI 4 "const_int_operand" ""))
+ (use (reg:QI 25))
+ (use (reg:QI 26))
+ (clobber (reg:CC_NOOV 21))])]
+ ""
+ "if (! TARGET_LOOP_UNSIGNED
+ && (unsigned HOST_WIDE_INT) INTVAL (operands[3]) > (1U << 31))
+ FAIL;
+ if (INTVAL (operands[4]) > 1 || ! TARGET_RPTB)
+ {
+ /* The C30 maximum iteration count for DB is 2^24. */
+ if (! TARGET_DB)
+ FAIL;
+ emit_insn (gen_decrement_and_branch_until_zero (operands[0],
+ operands[1]));
+ DONE;
+ }")
+
+; The current low overhead looping code is naff and is not failsafe
+; If you want RTPB instructions to be generated, apply the patches
+; from www.elec.canterbury.ac.nz/c4x. This will utilise the
+; doloop_begin and doloop_end patterns in this MD.
+(define_expand "decrement_and_branch_on_count"
+ [(parallel [(set (pc)
+ (if_then_else (ge (match_operand:QI 0 "register_operand" "")
+ (const_int 0))
+ (label_ref (match_operand 1 "" ""))
+ (pc)))
+ (set (match_dup 0)
+ (plus:QI (match_dup 0)
+ (const_int -1)))
+ (use (reg:QI 25))
+ (use (reg:QI 26))
+ (clobber (reg:CC_NOOV 21))])]
+ "0"
+ "")
+
+(define_expand "movstrqi_small2"
+ [(parallel [(set (mem:BLK (match_operand:BLK 0 "src_operand" ""))
+ (mem:BLK (match_operand:BLK 1 "src_operand" "")))
+ (use (match_operand:QI 2 "immediate_operand" ""))
+ (use (match_operand:QI 3 "immediate_operand" ""))
+ (clobber (match_operand:QI 4 "ext_low_reg_operand" ""))])]
+ ""
+ "
+ {
+ rtx src, dst, tmp;
+ rtx src_mem, dst_mem;
+ int len;
+ int i;
+
+ dst = operands[0];
+ src = operands[1];
+ len = INTVAL (operands[2]);
+ tmp = operands[4];
+
+ src_mem = gen_rtx_MEM (QImode, src);
+ dst_mem = gen_rtx_MEM (QImode, dst);
+
+ emit_insn (gen_movqi (tmp, src_mem));
+ emit_insn (gen_addqi3_noclobber (src, src, const1_rtx));
+ for (i = 1; i < len; i++)
+ {
+ emit_insn (gen_movqi_parallel (tmp, src_mem, dst_mem, tmp));
+ emit_insn (gen_addqi3_noclobber (src, src, const1_rtx));
+ emit_insn (gen_addqi3_noclobber (dst, dst, const1_rtx));
+ }
+ emit_insn (gen_movqi (dst_mem, tmp));
+ emit_insn (gen_addqi3_noclobber (dst, dst, const1_rtx));
+ DONE;
+ }
+ ")
+
+
+;
+; BLOCK MOVE
+; We should probably get RC loaded when using RPTB automagically...
+; There's probably no need to call _memcpy() if we don't get
+; a immediate operand for the size. We could do a better job here
+; than most memcpy() implementations.
+; operand 2 is the number of bytes
+; operand 3 is the shared alignment
+; operand 4 is a scratch register
+
+(define_insn "movstrqi_small"
+ [(set (mem:BLK (match_operand:QI 0 "addr_reg_operand" "+a"))
+ (mem:BLK (match_operand:QI 1 "addr_reg_operand" "+a")))
+ (use (match_operand:QI 2 "immediate_operand" "i"))
+ (use (match_operand:QI 3 "immediate_operand" ""))
+ (clobber (match_operand:QI 4 "ext_low_reg_operand" "=&q"))
+ (clobber (match_dup 0))
+ (clobber (match_dup 1))]
+ ""
+ "*
+ {
+ int i;
+ int len = INTVAL (operands[2]);
+ int first = 1;
+
+ for (i = 0; i < len; i++)
+ {
+ if (first)
+ output_asm_insn (\"ldiu\\t*%1++,%4\", operands);
+ else
+ output_asm_insn (\"|| ldi\\t*%1++,%4\", operands);
+ output_asm_insn (\"sti\\t%4,*%0++\", operands);
+ first = 0;
+ }
+ return \"\";
+ }
+ "
+ [(set_attr "type" "multi")])
+
+(define_insn "movstrqi_large"
+ [(set (mem:BLK (match_operand:QI 0 "addr_reg_operand" "+a"))
+ (mem:BLK (match_operand:QI 1 "addr_reg_operand" "+a")))
+ (use (match_operand:QI 2 "immediate_operand" "i"))
+ (use (match_operand:QI 3 "immediate_operand" ""))
+ (clobber (match_operand:QI 4 "ext_low_reg_operand" "=&q"))
+ (clobber (match_dup 0))
+ (clobber (match_dup 1))
+ (clobber (reg:QI 25))
+ (clobber (reg:QI 26))
+ (clobber (reg:QI 27))]
+ ""
+ "*
+ {
+ int len = INTVAL (operands[2]);
+
+ output_asm_insn (\"ldiu\\t*%1++,%4\", operands);
+ if (TARGET_RPTS_CYCLES (len))
+ {
+ output_asm_insn (\"rpts\\t%2-2\", operands);
+ output_asm_insn (\"sti\\t%4,*%0++\", operands);
+ output_asm_insn (\"|| ldi\\t*%1++,%4\", operands);
+ return \"sti\\t%4,*%0++\";
+ }
+ else
+ {
+ output_asm_insn (\"ldiu\\t%2-2,rc\", operands);
+ output_asm_insn (\"rptb\\t$+1\", operands);
+ output_asm_insn (\"sti\\t%4,*%0++\", operands);
+ output_asm_insn (\"|| ldi\\t*%1++,%4\", operands);
+
+ return \"sti\\t%4,*%0++\";
+ }
+ }
+ "
+ [(set_attr "type" "multi")])
+
+; Operand 2 is the count, operand 3 is the alignment.
+(define_expand "movstrqi"
+ [(parallel [(set (mem:BLK (match_operand:BLK 0 "src_operand" ""))
+ (mem:BLK (match_operand:BLK 1 "src_operand" "")))
+ (use (match_operand:QI 2 "immediate_operand" ""))
+ (use (match_operand:QI 3 "immediate_operand" ""))])]
+ ""
+ "
+ {
+ rtx tmp;
+ if (GET_CODE (operands[2]) != CONST_INT
+ || INTVAL (operands[2]) > 32767
+ || INTVAL (operands[2]) <= 0)
+ {
+ FAIL; /* Try to call _memcpy */
+ }
+
+ operands[0] = copy_to_mode_reg (Pmode, XEXP (operands[0], 0));
+ operands[1] = copy_to_mode_reg (Pmode, XEXP (operands[1], 0));
+ tmp = gen_reg_rtx (QImode);
+ if (INTVAL (operands[2]) < 8)
+ emit_insn (gen_movstrqi_small (operands[0], operands[1], operands[2],
+ operands[3], tmp));
+ else
+ {
+ emit_insn (gen_movstrqi_large (operands[0], operands[1], operands[2],
+ operands[3], tmp));
+ }
+ DONE;
+ }")
+
+
+(define_insn "*cmpstrqi"
+ [(set (match_operand:QI 0 "ext_reg_operand" "=d")
+ (compare:QI (mem:BLK (match_operand:QI 1 "addr_reg_operand" "+a"))
+ (mem:BLK (match_operand:QI 2 "addr_reg_operand" "+a"))))
+ (use (match_operand:QI 3 "immediate_operand" "i"))
+ (use (match_operand:QI 4 "immediate_operand" ""))
+ (clobber (match_operand:QI 5 "std_reg_operand" "=&c"))
+ (clobber (reg:QI 21))]
+ ""
+ "*
+ {
+ output_asm_insn (\"ldi\\t%3-1,%5\", operands);
+ output_asm_insn (\"$1:\tsubi3\\t*%1++,*%2++,%0\", operands);
+ output_asm_insn (\"dbeq\\t%5,$1\", operands);
+ return \"\";
+ }")
+
+(define_expand "cmpstrqi"
+ [(parallel [(set (match_operand:QI 0 "reg_operand" "")
+ (compare:QI (match_operand:BLK 1 "general_operand" "")
+ (match_operand:BLK 2 "general_operand" "")))
+ (use (match_operand:QI 3 "immediate_operand" ""))
+ (use (match_operand:QI 4 "immediate_operand" ""))
+ (clobber (match_dup 5))
+ (clobber (reg:QI 21))])]
+ ""
+ "
+{
+ if (GET_CODE (operands[3]) != CONST_INT
+ || INTVAL (operands[3]) > 32767
+ || INTVAL (operands[3]) <= 0)
+ {
+ FAIL;
+ }
+ operands[1] = copy_to_mode_reg (Pmode, XEXP (operands[1], 0));
+ operands[2] = copy_to_mode_reg (Pmode, XEXP (operands[2], 0));
+ operands[5] = gen_reg_rtx (QImode);
+}")
+
+;
+; TWO OPERAND LONG DOUBLE INSTRUCTIONS
+;
+
+(define_expand "movhf"
+ [(set (match_operand:HF 0 "src_operand" "")
+ (match_operand:HF 1 "src_operand" ""))]
+ ""
+ "if (c4x_emit_move_sequence (operands, HFmode))
+ DONE;")
+
+(define_insn "*movhf_noclobber_reg"
+ [(set (match_operand:HF 0 "reg_operand" "=h")
+ (match_operand:HF 1 "src_operand" "Hh"))]
+ "GET_CODE (operands[1]) != MEM"
+ "ldfu\\t%1,%0"
+ [(set_attr "type" "unary")])
+
+(define_insn "*movhf_noclobber"
+ [(set (match_operand:HF 0 "src_operand" "=h,m")
+ (match_operand:HF 1 "src_operand" "Hm,h"))]
+ "reg_operand (operands[0], HFmode) ^ reg_operand (operands[1], HFmode)"
+ "#"
+ [(set_attr "type" "multi,multi")])
+
+(define_insn "*movhf_test"
+ [(set (reg:CC 21)
+ (compare:CC (match_operand:HF 1 "reg_operand" "h")
+ (const_int 0)))
+ (clobber (match_scratch:HF 0 "=h"))]
+ ""
+ "ldf\\t%1,%0"
+ [(set_attr "type" "unarycc")])
+
+(define_insn "*movhf_set"
+ [(set (reg:CC 21)
+ (compare:CC (match_operand:HF 1 "reg_operand" "h")
+ (match_operand:HF 2 "fp_zero_operand" "G")))
+ (set (match_operand:HF 0 "reg_operand" "=h")
+ (match_dup 1))]
+ ""
+ "ldf\\t%1,%0"
+ [(set_attr "type" "unarycc")])
+
+(define_split
+ [(set (match_operand:HF 0 "reg_operand" "")
+ (match_operand:HF 1 "memory_operand" ""))]
+ "reload_completed"
+ [(set (match_dup 0) (float_extend:HF (match_dup 2)))
+ (set (match_dup 0) (unspec[(subreg:QI (match_dup 0) 0) (match_dup 3)] 8))]
+ "operands[2] = c4x_operand_subword (operands[1], 0, 1, HFmode);
+ operands[3] = c4x_operand_subword (operands[1], 1, 1, HFmode);
+ PUT_MODE (operands[2], QFmode);
+ PUT_MODE (operands[3], QImode);")
+
+(define_split
+ [(set (match_operand:HF 0 "reg_operand" "")
+ (match_operand:HF 1 "const_operand" ""))]
+ "reload_completed && 0"
+ [(set (match_dup 0) (float_extend:HF (match_dup 2)))
+ (set (match_dup 0) (unspec[(subreg:QI (match_dup 0) 0) (match_dup 3)] 8))]
+ "operands[2] = c4x_operand_subword (operands[1], 0, 1, HFmode);
+ operands[3] = c4x_operand_subword (operands[1], 1, 1, HFmode);
+ PUT_MODE (operands[2], QFmode);
+ PUT_MODE (operands[3], QImode);")
+
+(define_split
+ [(set (match_operand:HF 0 "memory_operand" "")
+ (match_operand:HF 1 "reg_operand" ""))]
+ "reload_completed"
+ [(set (match_dup 2) (float_truncate:QF (match_dup 1)))
+ (set (match_dup 3) (unspec [(match_dup 1)] 9))]
+ "operands[2] = c4x_operand_subword (operands[0], 0, 1, HFmode);
+ operands[3] = c4x_operand_subword (operands[0], 1, 1, HFmode);
+ PUT_MODE (operands[2], QFmode);
+ PUT_MODE (operands[3], QImode);")
+
+(define_insn "*loadhf_float"
+ [(set (match_operand:HF 0 "reg_operand" "=h")
+ (float_extend:HF (match_operand:QF 1 "src_operand" "fHm")))]
+ ""
+ "@
+ ldfu\\t%1,%0"
+ [(set_attr "type" "unary")])
+
+(define_insn "*loadhf_int"
+ [(set (match_operand:HF 0 "reg_operand" "=h")
+ (unspec[(subreg:QI (match_dup 0) 0)
+ (match_operand:QI 1 "src_operand" "rIm")] 8))]
+ ""
+ "@
+ ldiu\\t%1,%0"
+ [(set_attr "type" "unary")])
+
+(define_insn "*storehf_float"
+ [(set (match_operand:QF 0 "memory_operand" "=m")
+ (float_truncate:QF (match_operand:HF 1 "reg_operand" "h")))]
+ ""
+ "stf\\t%1,%0"
+ [(set_attr "type" "store")])
+
+(define_insn "*storehf_int"
+ [(set (match_operand:QI 0 "memory_operand" "=m")
+ (unspec [(match_operand:HF 1 "reg_operand" "h")] 9))]
+ ""
+ "@
+ sti\\t%1,%0"
+ [(set_attr "type" "store")])
+
+(define_insn "extendqfhf2"
+ [(set (match_operand:HF 0 "reg_operand" "=h")
+ (float_extend:HF (match_operand:QF 1 "reg_operand" "h")))]
+ ""
+ "ldfu\\t%1,%0"
+ [(set_attr "type" "unarycc")])
+
+(define_insn "trunchfqf2"
+ [(set (match_operand:QF 0 "reg_operand" "=h")
+ (float_truncate:QF (match_operand:HF 1 "reg_operand" "0")))
+ (clobber (reg:CC 21))]
+ ""
+ "andn\\t0ffh,%0"
+ [(set_attr "type" "unarycc")])
+
+;
+; PUSH/POP
+;
+(define_insn "*pushhf"
+ [(set (mem:HF (pre_inc:QI (reg:QI 20)))
+ (match_operand:HF 0 "reg_operand" "h"))]
+ ""
+ "#"
+ [(set_attr "type" "multi")])
+
+(define_split
+ [(set (mem:HF (pre_inc:QI (reg:QI 20)))
+ (match_operand:HF 0 "reg_operand" ""))]
+ "reload_completed"
+ [(set (mem:QF (pre_inc:QI (reg:QI 20)))
+ (float_truncate:QF (match_dup 0)))
+ (set (mem:QI (pre_inc:QI (reg:QI 20)))
+ (unspec [(match_dup 0)] 9))]
+ "")
+
+(define_insn "pushhf_trunc"
+ [(set (mem:QF (pre_inc:QI (reg:QI 20)))
+ (float_truncate:QF (match_operand:HF 0 "reg_operand" "h")))]
+ ""
+ "pushf\\t%0"
+ [(set_attr "type" "push")])
+
+(define_insn "pushhf_int"
+ [(set (mem:QI (pre_inc:QI (reg:QI 20)))
+ (unspec [(match_operand:HF 0 "reg_operand" "h")] 9))]
+ ""
+ "push\\t%0"
+ [(set_attr "type" "push")])
+
+; we can not use this because the popf will destroy the low 8 bits
+;(define_insn "*pophf"
+; [(set (match_operand:HF 0 "reg_operand" "=h")
+; (mem:HF (post_dec:QI (reg:QI 20))))
+; (clobber (reg:CC 21))]
+; ""
+; "#"
+; [(set_attr "type" "multi")])
+
+(define_split
+ [(set (match_operand:HF 0 "reg_operand" "")
+ (mem:HF (post_dec:QI (reg:QI 20))))
+ (clobber (reg:CC 21))]
+ "reload_completed"
+ [(parallel [(set (match_operand:HF 0 "reg_operand" "=h")
+ (float_extend:HF (mem:QF (post_dec:QI (reg:QI 20)))))
+ (clobber (reg:CC 21))])
+ (parallel [(set (match_dup 0)
+ (unspec[(subreg:QI (match_dup 0) 0)
+ (mem:QI (post_dec:QI (reg:QI 20)))] 8))
+ (clobber (reg:CC 21))])]
+ "")
+
+(define_insn "*pophf_int"
+ [(set (match_operand:HF 0 "reg_operand" "=h")
+ (unspec[(subreg:QI (match_dup 0) 0)
+ (mem:QI (post_dec:QI (reg:QI 20)))] 8))
+ (clobber (reg:CC 21))]
+ ""
+ "@
+ pop\\t%0"
+ [(set_attr "type" "pop")])
+
+(define_insn "*pophf_float"
+ [(set (match_operand:HF 0 "reg_operand" "=h")
+ (float_extend:HF (mem:QF (post_dec:QI (reg:QI 20)))))
+ (clobber (reg:CC 21))]
+ ""
+ "@
+ popf\\t%0"
+ [(set_attr "type" "unary")])
+
+;
+; FIX
+;
+(define_insn "fixhfqi_clobber"
+ [(set (match_operand:QI 0 "reg_operand" "=dc")
+ (fix:QI (match_operand:HF 1 "reg_or_const_operand" "hH")))
+ (clobber (reg:CC 21))]
+ ""
+ "fix\\t%1,%0"
+ [(set_attr "type" "unarycc")])
+
+;
+; ABSF
+;
+(define_expand "abshf2"
+ [(parallel [(set (match_operand:HF 0 "reg_operand" "")
+ (abs:HF (match_operand:HF 1 "reg_or_const_operand" "")))
+ (clobber (reg:CC_NOOV 21))])]
+""
+"")
+
+(define_insn "*abshf2_clobber"
+ [(set (match_operand:HF 0 "reg_operand" "=h")
+ (abs:HF (match_operand:HF 1 "reg_or_const_operand" "hH")))
+ (clobber (reg:CC_NOOV 21))]
+ ""
+ "absf\\t%1,%0"
+ [(set_attr "type" "unarycc")])
+
+(define_insn "*abshf2_test"
+ [(set (reg:CC_NOOV 21)
+ (compare:CC_NOOV (abs:HF (match_operand:HF 1 "reg_operand" "h"))
+ (match_operand:HF 2 "fp_zero_operand" "G")))
+ (clobber (match_scratch:HF 0 "=h"))]
+ ""
+ "absf\\t%1,%0"
+ [(set_attr "type" "unarycc")])
+
+(define_insn "*abshf2_set"
+ [(set (reg:CC_NOOV 21)
+ (compare:CC_NOOV (abs:HF (match_operand:HF 1 "reg_or_const_operand" "hH"))
+ (match_operand:HF 2 "fp_zero_operand" "G")))
+ (set (match_operand:HF 0 "reg_operand" "=h")
+ (abs:HF (match_dup 1)))]
+
+ ""
+ "absf\\t%1,%0"
+ [(set_attr "type" "unarycc")])
+
+;
+; NEGF
+;
+(define_expand "neghf2"
+ [(parallel [(set (match_operand:HF 0 "reg_operand" "")
+ (neg:HF (match_operand:HF 1 "reg_or_const_operand" "")))
+ (clobber (reg:CC 21))])]
+""
+"")
+
+(define_insn "*neghf2_clobber"
+ [(set (match_operand:HF 0 "reg_operand" "=h")
+ (neg:HF (match_operand:HF 1 "reg_or_const_operand" "hH")))
+ (clobber (reg:CC 21))]
+ ""
+ "negf\\t%1,%0"
+ [(set_attr "type" "unarycc")])
+
+(define_insn "*neghf2_test"
+ [(set (reg:CC 21)
+ (compare:CC (neg:HF (match_operand:HF 1 "reg_or_const_operand" "hH"))
+ (match_operand:HF 2 "fp_zero_operand" "G")))
+ (clobber (match_scratch:HF 0 "=h"))]
+ ""
+ "negf\\t%1,%0"
+ [(set_attr "type" "unarycc")])
+
+(define_insn "*neghf2_set"
+ [(set (reg:CC 21)
+ (compare:CC (neg:HF (match_operand:HF 1 "reg_or_const_operand" "hH"))
+ (match_operand:HF 2 "fp_zero_operand" "G")))
+ (set (match_operand:HF 0 "reg_operand" "=h")
+ (neg:HF (match_dup 1)))]
+ ""
+ "negf\\t%1,%0"
+ [(set_attr "type" "unarycc")])
+
+;
+; RCPF
+;
+(define_insn "*rcpfhf_clobber"
+ [(set (match_operand:HF 0 "reg_operand" "=h")
+ (unspec [(match_operand:HF 1 "reg_or_const_operand" "hH")] 5))
+ (clobber (reg:CC_NOOV 21))]
+ "! TARGET_C3X"
+ "rcpf\\t%1,%0"
+ [(set_attr "type" "unarycc")])
+
+;
+; RSQRF
+;
+(define_insn "*rsqrfhf_clobber"
+ [(set (match_operand:HF 0 "reg_operand" "=h")
+ (unspec [(match_operand:HF 1 "reg_or_const_operand" "hH")] 10))
+ (clobber (reg:CC_NOOV 21))]
+ "! TARGET_C3X"
+ "rsqrf\\t%1,%0"
+ [(set_attr "type" "unarycc")])
+
+;
+; RNDF
+;
+(define_insn "*rndhf_clobber"
+ [(set (match_operand:HF 0 "reg_operand" "=h")
+ (unspec [(match_operand:HF 1 "reg_or_const_operand" "hH")] 6))
+ (clobber (reg:CC_NOOV 21))]
+ "! TARGET_C3X"
+ "rnd\\t%1,%0"
+ [(set_attr "type" "unarycc")])
+
+
+; Inlined float square root for C4x
+(define_expand "sqrthf2_inline"
+ [(parallel [(set (match_dup 2)
+ (unspec [(match_operand:HF 1 "reg_operand" "")] 10))
+ (clobber (reg:CC_NOOV 21))])
+ (parallel [(set (match_dup 3) (mult:HF (match_dup 5) (match_dup 1)))
+ (clobber (reg:CC_NOOV 21))])
+ (parallel [(set (match_dup 4) (mult:HF (match_dup 2) (match_dup 3)))
+ (clobber (reg:CC_NOOV 21))])
+ (parallel [(set (match_dup 4) (mult:HF (match_dup 2) (match_dup 4)))
+ (clobber (reg:CC_NOOV 21))])
+ (parallel [(set (match_dup 4) (minus:HF (match_dup 6) (match_dup 4)))
+ (clobber (reg:CC_NOOV 21))])
+ (parallel [(set (match_dup 2) (mult:HF (match_dup 2) (match_dup 4)))
+ (clobber (reg:CC_NOOV 21))])
+ (parallel [(set (match_dup 4) (mult:HF (match_dup 2) (match_dup 3)))
+ (clobber (reg:CC_NOOV 21))])
+ (parallel [(set (match_dup 4) (mult:HF (match_dup 2) (match_dup 4)))
+ (clobber (reg:CC_NOOV 21))])
+ (parallel [(set (match_dup 4) (minus:HF (match_dup 6) (match_dup 4)))
+ (clobber (reg:CC_NOOV 21))])
+ (parallel [(set (match_dup 2) (mult:HF (match_dup 2) (match_dup 4)))
+ (clobber (reg:CC_NOOV 21))])
+ (parallel [(set (match_operand:HF 0 "reg_operand" "")
+ (mult:HF (match_dup 2) (match_dup 1)))
+ (clobber (reg:CC_NOOV 21))])]
+ "! TARGET_C3X"
+ "
+ operands[2] = gen_reg_rtx (HFmode);
+ operands[3] = gen_reg_rtx (HFmode);
+ operands[4] = gen_reg_rtx (HFmode);
+ operands[5] = immed_real_const_1 (REAL_VALUE_ATOF (\"0.5\", HFmode), HFmode);
+ operands[6] = immed_real_const_1 (REAL_VALUE_ATOF (\"1.5\", HFmode), HFmode);
+ ")
+
+
+(define_expand "sqrthf2"
+ [(parallel [(set (match_operand:HF 0 "reg_operand" "")
+ (sqrt:HF (match_operand:HF 1 "reg_operand" "")))
+ (clobber (reg:CC 21))])]
+ ""
+ "if (TARGET_C3X || ! TARGET_INLINE)
+ FAIL;
+ else
+ {
+ emit_insn (gen_sqrthf2_inline (operands[0], operands[1]));
+ DONE;
+ }
+ ")
+
+(define_expand "fix_trunchfhi2"
+ [(parallel [(set (match_operand:HI 0 "reg_operand" "")
+ (fix:HI (match_operand:HF 1 "reg_operand" "")))
+ (clobber (reg:CC 21))])]
+ ""
+ "c4x_emit_libcall (FIX_TRUNCHFHI2_LIBCALL, FIX, HImode, HFmode, 2, operands);
+ DONE;")
+
+(define_expand "fixuns_trunchfhi2"
+ [(parallel [(set (match_operand:HI 0 "reg_operand" "")
+ (unsigned_fix:HI (match_operand:HF 1 "reg_operand" "")))
+ (clobber (reg:CC 21))])]
+ ""
+ "c4x_emit_libcall (FIXUNS_TRUNCHFHI2_LIBCALL, UNSIGNED_FIX,
+ HImode, HFmode, 2, operands);
+ DONE;")
+
+;
+; THREE OPERAND LONG DOUBLE INSTRUCTIONS
+;
+
+;
+; ADDF
+;
+(define_insn "addhf3"
+ [(set (match_operand:HF 0 "reg_operand" "=h,?h")
+ (plus:HF (match_operand:HF 1 "reg_operand" "%0,h")
+ (match_operand:HF 2 "reg_or_const_operand" "H,h")))
+ (clobber (reg:CC_NOOV 21))]
+ ""
+ "@
+ addf\\t%2,%0
+ addf3\\t%2,%1,%0"
+ [(set_attr "type" "binarycc,binarycc")])
+
+;
+; SUBF
+;
+(define_insn "subhf3"
+ [(set (match_operand:HF 0 "reg_operand" "=h,h,?h")
+ (minus:HF (match_operand:HF 1 "reg_or_const_operand" "0,H,h")
+ (match_operand:HF 2 "reg_or_const_operand" "H,0,h")))
+ (clobber (reg:CC_NOOV 21))]
+ ""
+ "@
+ subf\\t%2,%0
+ subrf\\t%1,%0
+ subf3\\t%2,%1,%0"
+ [(set_attr "type" "binarycc,binarycc,binarycc")])
+
+;
+; MULF
+;
+; The C3x MPYF only uses 24 bit precision while the C4x uses 32 bit precison.
+;
+(define_expand "mulhf3"
+ [(parallel [(set (match_operand:HF 0 "reg_operand" "=h")
+ (mult:HF (match_operand:HF 1 "reg_operand" "h")
+ (match_operand:HF 2 "reg_operand" "h")))
+ (clobber (reg:CC_NOOV 21))])]
+ ""
+ "if (TARGET_C3X)
+ {
+ c4x_emit_libcall3 (MULHF3_LIBCALL, MULT, HFmode, operands);
+ DONE;
+ }
+ ")
+
+(define_insn "*mulhf3_c40"
+ [(set (match_operand:HF 0 "reg_operand" "=h,?h")
+ (mult:HF (match_operand:HF 1 "reg_operand" "%0,h")
+ (match_operand:HF 2 "reg_or_const_operand" "hH,h")))
+ (clobber (reg:CC_NOOV 21))]
+ ""
+ "@
+ mpyf\\t%2,%0
+ mpyf3\\t%2,%1,%0"
+ [(set_attr "type" "binarycc,binarycc")])
+
+;
+; CMPF
+;
+(define_expand "cmphf"
+ [(set (reg:CC 21)
+ (compare:CC (match_operand:HF 0 "reg_operand" "")
+ (match_operand:HF 1 "reg_or_const_operand" "")))]
+ ""
+ "c4x_compare_op0 = operands[0];
+ c4x_compare_op1 = operands[1];
+ DONE;")
+
+(define_insn "*cmphf"
+ [(set (reg:CC 21)
+ (compare:CC (match_operand:HF 0 "reg_operand" "h")
+ (match_operand:HF 1 "reg_or_const_operand" "hH")))]
+ ""
+ "cmpf\\t%1,%0"
+ [(set_attr "type" "compare")])
+
+(define_insn "*cmphf_noov"
+ [(set (reg:CC_NOOV 21)
+ (compare:CC_NOOV (match_operand:HF 0 "reg_operand" "h")
+ (match_operand:HF 1 "reg_or_const_operand" "hH")))]
+ ""
+ "cmpf\\t%1,%0"
+ [(set_attr "type" "compare")])
+
+; Inlined float divide for C4x
+(define_expand "divhf3_inline"
+ [(parallel [(set (match_dup 3)
+ (unspec [(match_operand:HF 2 "reg_operand" "")] 5))
+ (clobber (reg:CC_NOOV 21))])
+ (parallel [(set (match_dup 4) (mult:HF (match_dup 2) (match_dup 3)))
+ (clobber (reg:CC_NOOV 21))])
+ (parallel [(set (match_dup 4) (minus:HF (match_dup 5) (match_dup 4)))
+ (clobber (reg:CC_NOOV 21))])
+ (parallel [(set (match_dup 3) (mult:HF (match_dup 3) (match_dup 4)))
+ (clobber (reg:CC_NOOV 21))])
+ (parallel [(set (match_dup 4) (mult:HF (match_dup 2) (match_dup 3)))
+ (clobber (reg:CC_NOOV 21))])
+ (parallel [(set (match_dup 4) (minus:HF (match_dup 5) (match_dup 4)))
+ (clobber (reg:CC_NOOV 21))])
+ (parallel [(set (match_dup 3) (mult:HF (match_dup 3) (match_dup 4)))
+ (clobber (reg:CC_NOOV 21))])
+ (parallel [(set (match_operand:HF 0 "reg_operand" "")
+ (mult:HF (match_operand:HF 1 "reg_operand" "")
+ (match_dup 3)))
+ (clobber (reg:CC_NOOV 21))])]
+ "! TARGET_C3X"
+ "
+ operands[3] = gen_reg_rtx (HFmode);
+ operands[4] = gen_reg_rtx (HFmode);
+ operands[5] = CONST2_RTX (HFmode);
+ ")
+
+(define_expand "divhf3"
+ [(parallel [(set (match_operand:HF 0 "reg_operand" "")
+ (div:HF (match_operand:HF 1 "reg_operand" "")
+ (match_operand:HF 2 "reg_operand" "")))
+ (clobber (reg:CC 21))])]
+ ""
+ "if (TARGET_C3X || ! TARGET_INLINE)
+ {
+ c4x_emit_libcall3 (DIVHF3_LIBCALL, DIV, HFmode, operands);
+ DONE;
+ }
+ else
+ {
+ emit_insn (gen_divhf3_inline (operands[0], operands[1], operands[2]));
+ DONE;
+ }
+ ")
+
+
+;
+; TWO OPERAND LONG LONG INSTRUCTIONS
+;
+
+(define_insn "*movhi_stik"
+ [(set (match_operand:HI 0 "memory_operand" "=m")
+ (match_operand:HI 1 "stik_const_operand" "K"))]
+ "! TARGET_C3X"
+ "#"
+ [(set_attr "type" "multi")])
+
+; We could load some constants using define_splits for the C30
+; in the large memory model---these would emit shift and or insns.
+(define_expand "movhi"
+ [(set (match_operand:HI 0 "src_operand" "")
+ (match_operand:HI 1 "src_operand" ""))]
+ ""
+ "if (c4x_emit_move_sequence (operands, HImode))
+ DONE;")
+
+; The constraints for movhi must include 'r' if we don't
+; restrict HImode regnos to start on an even number, since
+; we can get RC, R8 allocated as a pair. We want more
+; votes for FP_REGS so we use dr as the constraints.
+(define_insn "*movhi_noclobber"
+ [(set (match_operand:HI 0 "src_operand" "=dr,m")
+ (match_operand:HI 1 "src_operand" "drIm,r"))]
+ "reg_operand (operands[0], HImode)
+ || reg_operand (operands[1], HImode)"
+ "#"
+ [(set_attr "type" "multi,multi")])
+
+; This will fail miserably if the destination register is used in the
+; source memory address.
+; The usual strategy in this case is to swap the order of insns we emit,
+; however, this will fail if we have an autoincrement memory address.
+; For example:
+; ldi *ar0++, ar0
+; ldi *ar0++, ar1
+;
+; We could convert this to
+; ldi *ar0(1), ar1
+; ldi *ar0, ar0
+;
+; However, things are likely to be very screwed up if we get this.
+
+(define_split
+ [(set (match_operand:HI 0 "src_operand" "")
+ (match_operand:HI 1 "src_operand" ""))]
+ "reload_completed
+ && (reg_operand (operands[0], HImode)
+ || reg_operand (operands[1], HImode)
+ || stik_const_operand (operands[1], HImode))"
+ [(set (match_dup 2) (match_dup 4))
+ (set (match_dup 3) (match_dup 5))]
+ "operands[2] = c4x_operand_subword (operands[0], 0, 1, HImode);
+ operands[3] = c4x_operand_subword (operands[0], 1, 1, HImode);
+ operands[4] = c4x_operand_subword (operands[1], 0, 1, HImode);
+ operands[5] = c4x_operand_subword (operands[1], 1, 1, HImode);
+ if (reg_overlap_mentioned_p (operands[2], operands[5]))
+ {
+ /* Swap order of move insns. */
+ rtx tmp;
+ tmp = operands[2];
+ operands[2] =operands[3];
+ operands[3] = tmp;
+ tmp = operands[4];
+ operands[4] =operands[5];
+ operands[5] = tmp;
+ }")
+
+
+(define_insn "extendqihi2"
+ [(set (match_operand:HI 0 "reg_operand" "=dc")
+ (sign_extend:HI (match_operand:QI 1 "src_operand" "rIm")))
+ (clobber (reg:CC 21))]
+ ""
+ "#"
+ [(set_attr "type" "multi")])
+
+(define_split
+ [(set (match_operand:HI 0 "reg_operand" "=?dc")
+ (sign_extend:HI (match_operand:QI 1 "src_operand" "rIm")))
+ (clobber (reg:CC 21))]
+ "reload_completed && TARGET_C3X"
+ [(set (match_dup 2) (match_dup 1))
+ (set (match_dup 3) (match_dup 2))
+ (parallel [(set (match_dup 3) (ashiftrt:QI (match_dup 3) (const_int 31)))
+ (clobber (reg:CC 21))])]
+ "operands[2] = c4x_operand_subword (operands[0], 0, 0, HImode);
+ operands[3] = c4x_operand_subword (operands[0], 1, 0, HImode);")
+
+(define_split
+ [(set (match_operand:HI 0 "reg_operand" "=?dc")
+ (sign_extend:HI (match_operand:QI 1 "src_operand" "rIm")))
+ (clobber (reg:CC 21))]
+ "reload_completed && ! TARGET_C3X"
+ [(set (match_dup 2) (match_dup 1))
+ (parallel [(set (match_dup 3) (ashiftrt:QI (match_dup 2) (const_int 31)))
+ (clobber (reg:CC 21))])]
+ "operands[2] = c4x_operand_subword (operands[0], 0, 0, HImode);
+ operands[3] = c4x_operand_subword (operands[0], 1, 0, HImode);")
+
+(define_insn "zero_extendqihi2"
+ [(set (match_operand:HI 0 "reg_operand" "=?dc")
+ (zero_extend:HI (match_operand:QI 1 "src_operand" "rIm")))
+ (clobber (reg:CC 21))]
+ ""
+ "#"
+ [(set_attr "type" "multi")])
+
+; If operand0 and operand1 are the same register we don't need
+; the first set.
+(define_split
+ [(set (match_operand:HI 0 "reg_operand" "=?dc")
+ (zero_extend:HI (match_operand:QI 1 "src_operand" "rIm")))
+ (clobber (reg:CC 21))]
+ "reload_completed"
+ [(set (match_dup 2) (match_dup 1))
+ (set (match_dup 3) (const_int 0))]
+ "operands[2] = c4x_operand_subword (operands[0], 0, 0, HImode);
+ operands[3] = c4x_operand_subword (operands[0], 1, 0, HImode);")
+
+;
+; PUSH/POP
+;
+(define_insn "*pushhi"
+ [(set (mem:HI (pre_inc:QI (reg:QI 20)))
+ (match_operand:HI 0 "reg_operand" "r"))]
+ ""
+ "#"
+ [(set_attr "type" "multi")])
+
+(define_split
+ [(set (mem:HI (pre_inc:QI (reg:QI 20)))
+ (match_operand:HI 0 "reg_operand" ""))]
+ "reload_completed"
+ [(set (mem:QI (pre_inc:QI (reg:QI 20))) (match_dup 2))
+ (set (mem:QI (pre_inc:QI (reg:QI 20))) (match_dup 3))]
+ "operands[2] = c4x_operand_subword (operands[0], 0, 0, HImode);
+ operands[3] = c4x_operand_subword (operands[0], 1, 0, HImode);")
+
+(define_insn "*pophi"
+ [(set (match_operand:HI 0 "reg_operand" "=r")
+ (mem:HI (post_dec:QI (reg:QI 20))))
+ (clobber (reg:CC 21))]
+ ""
+ "#"
+ [(set_attr "type" "multi")])
+
+(define_split
+ [(set (match_operand:HI 0 "reg_operand" "")
+ (mem:HI (pre_inc:QI (reg:QI 20))))]
+ "reload_completed"
+ [(set (match_dup 2) (mem:QI (pre_inc:QI (reg:QI 20))))
+ (set (match_dup 3) (mem:QI (pre_inc:QI (reg:QI 20))))]
+ "operands[2] = c4x_operand_subword (operands[0], 0, 0, HImode);
+ operands[3] = c4x_operand_subword (operands[0], 1, 0, HImode);")
+
+;
+; NEG
+;
+(define_insn "neghi2"
+ [(set (match_operand:HI 0 "ext_reg_operand" "=d")
+ (neg:HI (match_operand:HI 1 "src_operand" "rm")))
+ (clobber (reg:CC_NOOV 21))]
+ ""
+ "#"
+ [(set_attr "type" "multi")])
+
+(define_split
+ [(set (match_operand:HI 0 "ext_reg_operand" "")
+ (neg:HI (match_operand:HI 1 "src_operand" "")))
+ (clobber (reg:CC_NOOV 21))]
+ "reload_completed"
+ [(parallel [(set (reg:CC_NOOV 21)
+ (compare:CC_NOOV (neg:QI (match_dup 3))
+ (const_int 0)))
+ (set (match_dup 2) (neg:QI (match_dup 3)))])
+ (parallel [(set (match_dup 4) (neg:QI (match_dup 5)))
+ (use (reg:CC_NOOV 21))
+ (clobber (reg:CC_NOOV 21))])]
+ "operands[2] = c4x_operand_subword (operands[0], 0, 1, HImode);
+ operands[3] = c4x_operand_subword (operands[1], 0, 1, HImode);
+ operands[4] = c4x_operand_subword (operands[0], 1, 1, HImode);
+ operands[5] = c4x_operand_subword (operands[1], 1, 1, HImode);")
+
+(define_insn "one_cmplhi2"
+ [(set (match_operand:HI 0 "reg_operand" "=r")
+ (not:HI (match_operand:HI 1 "src_operand" "rm")))
+ (clobber (reg:CC 21))]
+ ""
+ "#"
+ [(set_attr "type" "multi")])
+
+(define_split
+ [(set (match_operand:HI 0 "reg_operand" "")
+ (not:HI (match_operand:HI 1 "src_operand" "")))
+ (clobber (reg:CC 21))]
+ "reload_completed"
+ [(parallel [(set (match_dup 2) (not:QI (match_dup 3)))
+ (clobber (reg:CC 21))])
+ (parallel [(set (match_dup 4) (not:QI (match_dup 5)))
+ (clobber (reg:CC 21))])]
+ "operands[2] = c4x_operand_subword (operands[0], 0, 1, HImode);
+ operands[3] = c4x_operand_subword (operands[1], 0, 1, HImode);
+ operands[4] = c4x_operand_subword (operands[0], 1, 1, HImode);
+ operands[5] = c4x_operand_subword (operands[1], 1, 1, HImode);")
+
+(define_expand "floathiqf2"
+ [(parallel [(set (match_operand:QF 0 "reg_operand" "")
+ (float:QF (match_operand:HI 1 "src_operand" "")))
+ (clobber (reg:CC 21))])]
+ ""
+ "c4x_emit_libcall (FLOATHIQF2_LIBCALL, FLOAT, QFmode, HImode, 2, operands);
+ DONE;")
+
+(define_expand "floatunshiqf2"
+ [(parallel [(set (match_operand:QF 0 "reg_operand" "")
+ (unsigned_float:QF (match_operand:HI 1 "src_operand" "")))
+ (clobber (reg:CC 21))])]
+ ""
+ "c4x_emit_libcall (FLOATUNSHIQF2_LIBCALL, UNSIGNED_FLOAT,
+ QFmode, HImode, 2, operands);
+ DONE;")
+
+(define_expand "floathihf2"
+ [(parallel [(set (match_operand:HF 0 "reg_operand" "")
+ (float:HF (match_operand:HI 1 "src_operand" "")))
+ (clobber (reg:CC 21))])]
+ ""
+ "c4x_emit_libcall (FLOATHIHF2_LIBCALL, FLOAT, HFmode, HImode, 2, operands);
+ DONE;")
+
+(define_expand "floatunshihf2"
+ [(parallel [(set (match_operand:HF 0 "reg_operand" "")
+ (unsigned_float:HF (match_operand:HI 1 "src_operand" "")))
+ (clobber (reg:CC 21))])]
+ ""
+ "c4x_emit_libcall (FLOATUNSHIHF2_LIBCALL, UNSIGNED_FLOAT,
+ HFmode, HImode, 2, operands);
+ DONE;")
+
+
+;
+; THREE OPERAND LONG LONG INSTRUCTIONS
+;
+
+(define_expand "addhi3"
+ [(parallel [(set (match_operand:HI 0 "ext_reg_operand" "")
+ (plus:HI (match_operand:HI 1 "src_operand" "")
+ (match_operand:HI 2 "src_operand" "")))
+ (clobber (reg:CC_NOOV 21))])]
+ ""
+ "legitimize_operands (PLUS, operands, HImode);")
+
+(define_insn "*addhi3_clobber"
+ [(set (match_operand:HI 0 "ext_reg_operand" "=d,d,?d")
+ (plus:HI (match_operand:HI 1 "src_operand" "%0,rR,rS<>")
+ (match_operand:HI 2 "src_operand" "rm,R,rS<>")))
+ (clobber (reg:CC_NOOV 21))]
+ "valid_operands (PLUS, operands, HImode)"
+ "#"
+ [(set_attr "type" "multi,multi,multi")])
+
+(define_split
+ [(set (match_operand:HI 0 "ext_reg_operand" "")
+ (plus:HI (match_operand:HI 1 "src_operand" "")
+ (match_operand:HI 2 "src_operand" "")))
+ (clobber (reg:CC_NOOV 21))]
+ "reload_completed"
+ [(parallel [(set (reg:CC_NOOV 21)
+ (compare:CC_NOOV (plus:QI (match_dup 4) (match_dup 5))
+ (const_int 0)))
+ (set (match_dup 3) (plus:QI (match_dup 4) (match_dup 5)))])
+ (parallel [(set (match_dup 6) (plus:QI (match_dup 7) (match_dup 8)))
+ (use (reg:CC_NOOV 21))
+ (clobber (reg:CC_NOOV 21))])]
+ "operands[3] = c4x_operand_subword (operands[0], 0, 1, HImode);
+ operands[4] = c4x_operand_subword (operands[1], 0, 1, HImode);
+ operands[5] = c4x_operand_subword (operands[2], 0, 1, HImode);
+ operands[6] = c4x_operand_subword (operands[0], 1, 1, HImode);
+ operands[7] = c4x_operand_subword (operands[1], 1, 1, HImode);
+ operands[8] = c4x_operand_subword (operands[2], 1, 1, HImode);")
+
+(define_expand "subhi3"
+ [(parallel [(set (match_operand:HI 0 "ext_reg_operand" "")
+ (minus:HI (match_operand:HI 1 "src_operand" "")
+ (match_operand:HI 2 "src_operand" "")))
+ (clobber (reg:CC_NOOV 21))])]
+ ""
+ "legitimize_operands (MINUS, operands, HImode);")
+
+
+(define_insn "*subhi3_clobber"
+ [(set (match_operand:HI 0 "ext_reg_operand" "=d,d,?d")
+ (minus:HI (match_operand:HI 1 "src_operand" "0,rR,rS<>")
+ (match_operand:HI 2 "src_operand" "rm,R,rS<>")))
+ (clobber (reg:CC_NOOV 21))]
+ "valid_operands (MINUS, operands, HImode)"
+ "#"
+ [(set_attr "type" "multi,multi,multi")])
+
+(define_split
+ [(set (match_operand:HI 0 "ext_reg_operand" "")
+ (minus:HI (match_operand:HI 1 "src_operand" "")
+ (match_operand:HI 2 "src_operand" "")))
+ (clobber (reg:CC_NOOV 21))]
+ "reload_completed"
+ [(parallel [(set (reg:CC_NOOV 21)
+ (compare:CC_NOOV (minus:QI (match_dup 4) (match_dup 5))
+ (const_int 0)))
+ (set (match_dup 3) (minus:QI (match_dup 4) (match_dup 5)))])
+ (parallel [(set (match_dup 6) (minus:QI (match_dup 7) (match_dup 8)))
+ (use (reg:CC_NOOV 21))
+ (clobber (reg:CC_NOOV 21))])]
+ "operands[3] = c4x_operand_subword (operands[0], 0, 1, HImode);
+ operands[4] = c4x_operand_subword (operands[1], 0, 1, HImode);
+ operands[5] = c4x_operand_subword (operands[2], 0, 1, HImode);
+ operands[6] = c4x_operand_subword (operands[0], 1, 1, HImode);
+ operands[7] = c4x_operand_subword (operands[1], 1, 1, HImode);
+ operands[8] = c4x_operand_subword (operands[2], 1, 1, HImode);")
+
+(define_expand "iorhi3"
+ [(parallel [(set (match_operand:HI 0 "reg_operand" "")
+ (ior:HI (match_operand:HI 1 "src_operand" "")
+ (match_operand:HI 2 "src_operand" "")))
+ (clobber (reg:CC 21))])]
+ ""
+ "legitimize_operands (IOR, operands, HImode);")
+
+(define_insn "*iorhi3_clobber"
+ [(set (match_operand:HI 0 "reg_operand" "=d,d,?d")
+ (ior:HI (match_operand:HI 1 "src_operand" "%0,rR,rS<>")
+ (match_operand:HI 2 "src_operand" "rm,R,rS<>")))
+ (clobber (reg:CC 21))]
+ "valid_operands (IOR, operands, HImode)"
+ "#"
+ [(set_attr "type" "multi,multi,multi")])
+
+(define_split
+ [(set (match_operand:HI 0 "reg_operand" "")
+ (ior:HI (match_operand:HI 1 "src_operand" "")
+ (match_operand:HI 2 "src_operand" "")))
+ (clobber (reg:CC 21))]
+ "reload_completed"
+ [(parallel [(set (match_dup 3) (ior:QI (match_dup 4) (match_dup 5)))
+ (clobber (reg:CC 21))])
+ (parallel [(set (match_dup 6) (ior:QI (match_dup 7) (match_dup 8)))
+ (clobber (reg:CC 21))])]
+ "operands[3] = c4x_operand_subword (operands[0], 0, 1, HImode);
+ operands[4] = c4x_operand_subword (operands[1], 0, 1, HImode);
+ operands[5] = c4x_operand_subword (operands[2], 0, 1, HImode);
+ operands[6] = c4x_operand_subword (operands[0], 1, 1, HImode);
+ operands[7] = c4x_operand_subword (operands[1], 1, 1, HImode);
+ operands[8] = c4x_operand_subword (operands[2], 1, 1, HImode);")
+
+(define_expand "andhi3"
+ [(parallel [(set (match_operand:HI 0 "reg_operand" "")
+ (and:HI (match_operand:HI 1 "src_operand" "")
+ (match_operand:HI 2 "src_operand" "")))
+ (clobber (reg:CC 21))])]
+ ""
+ "legitimize_operands (AND, operands, HImode);")
+
+(define_insn "*andhi3_clobber"
+ [(set (match_operand:HI 0 "reg_operand" "=d,d,?d")
+ (and:HI (match_operand:HI 1 "src_operand" "%0,rR,rS<>")
+ (match_operand:HI 2 "src_operand" "rm,R,rS<>")))
+ (clobber (reg:CC 21))]
+ "valid_operands (AND, operands, HImode)"
+ "#"
+ [(set_attr "type" "multi,multi,multi")])
+
+(define_split
+ [(set (match_operand:HI 0 "reg_operand" "")
+ (and:HI (match_operand:HI 1 "src_operand" "")
+ (match_operand:HI 2 "src_operand" "")))
+ (clobber (reg:CC 21))]
+ "reload_completed"
+ [(parallel [(set (match_dup 3) (and:QI (match_dup 4) (match_dup 5)))
+ (clobber (reg:CC 21))])
+ (parallel [(set (match_dup 6) (and:QI (match_dup 7) (match_dup 8)))
+ (clobber (reg:CC 21))])]
+ "operands[3] = c4x_operand_subword (operands[0], 0, 1, HImode);
+ operands[4] = c4x_operand_subword (operands[1], 0, 1, HImode);
+ operands[5] = c4x_operand_subword (operands[2], 0, 1, HImode);
+ operands[6] = c4x_operand_subword (operands[0], 1, 1, HImode);
+ operands[7] = c4x_operand_subword (operands[1], 1, 1, HImode);
+ operands[8] = c4x_operand_subword (operands[2], 1, 1, HImode);")
+
+(define_expand "xorhi3"
+ [(parallel [(set (match_operand:HI 0 "reg_operand" "")
+ (xor:HI (match_operand:HI 1 "src_operand" "")
+ (match_operand:HI 2 "src_operand" "")))
+ (clobber (reg:CC 21))])]
+ ""
+ "legitimize_operands (XOR, operands, HImode);")
+
+
+(define_insn "*xorhi3_clobber"
+ [(set (match_operand:HI 0 "reg_operand" "=d,d,?d")
+ (xor:HI (match_operand:HI 1 "src_operand" "%0,rR,rS<>")
+ (match_operand:HI 2 "src_operand" "rm,R,rS<>")))
+ (clobber (reg:CC 21))]
+ "valid_operands (XOR, operands, HImode)"
+ "#"
+ [(set_attr "type" "multi,multi,multi")])
+
+(define_split
+ [(set (match_operand:HI 0 "reg_operand" "")
+ (xor:HI (match_operand:HI 1 "src_operand" "")
+ (match_operand:HI 2 "src_operand" "")))
+ (clobber (reg:CC 21))]
+ "reload_completed"
+ [(parallel [(set (match_dup 3) (xor:QI (match_dup 4) (match_dup 5)))
+ (clobber (reg:CC 21))])
+ (parallel [(set (match_dup 6) (xor:QI (match_dup 7) (match_dup 8)))
+ (clobber (reg:CC 21))])]
+ "operands[3] = c4x_operand_subword (operands[0], 0, 1, HImode);
+ operands[4] = c4x_operand_subword (operands[1], 0, 1, HImode);
+ operands[5] = c4x_operand_subword (operands[2], 0, 1, HImode);
+ operands[6] = c4x_operand_subword (operands[0], 1, 1, HImode);
+ operands[7] = c4x_operand_subword (operands[1], 1, 1, HImode);
+ operands[8] = c4x_operand_subword (operands[2], 1, 1, HImode);")
+
+(define_expand "ashlhi3"
+ [(parallel [(set (match_operand:HI 0 "reg_operand" "")
+ (ashift:HI (match_operand:HI 1 "src_operand" "")
+ (match_operand:QI 2 "src_operand" "")))
+ (clobber (reg:CC 21))])]
+ ""
+ "if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) >= 32)
+ {
+ rtx op0hi = operand_subword (operands[0], 1, 0, HImode);
+ rtx op0lo = operand_subword (operands[0], 0, 0, HImode);
+ rtx op1lo = operand_subword (operands[1], 0, 0, HImode);
+ rtx count = GEN_INT ((INTVAL (operands[2]) - 32));
+
+ if (INTVAL (count))
+ emit_insn (gen_ashlqi3 (op0hi, op1lo, count));
+ else
+ emit_insn (gen_movqi (op0hi, op1lo));
+ emit_insn (gen_movqi (op0lo, const0_rtx));
+ DONE;
+ }
+ if (! REG_P (operands[1]))
+ operands[1] = force_reg (HImode, operands[1]);
+ emit_insn (gen_ashlhi3_reg (operands[0], operands[1], operands[2]));
+ DONE;
+ ")
+
+; %0.lo = %1.lo << %2
+; %0.hi = (%1.hi << %2 ) | (%1.lo >> (32 - %2))
+; This algorithm should work for shift counts greater than 32
+(define_expand "ashlhi3_reg"
+ [(use (match_operand:HI 1 "reg_operand" ""))
+ (use (match_operand:HI 0 "reg_operand" ""))
+ /* If the shift count is greater than 32 this will give zero. */
+ (parallel [(set (match_dup 7)
+ (ashift:QI (match_dup 3)
+ (match_operand:QI 2 "reg_operand" "")))
+ (clobber (reg:CC 21))])
+ /* If the shift count is greater than 32 this will give zero. */
+ (parallel [(set (match_dup 8)
+ (ashift:QI (match_dup 4) (match_dup 2)))
+ (clobber (reg:CC 21))])
+ (parallel [(set (match_dup 10)
+ (plus:QI (match_dup 2) (const_int -32)))
+ (clobber (reg:CC_NOOV 21))])
+ /* If the shift count is greater than 32 this will do a left shift. */
+ (parallel [(set (match_dup 9)
+ (lshiftrt:QI (match_dup 3) (neg:QI (match_dup 10))))
+ (clobber (reg:CC 21))])
+ (set (match_dup 5) (match_dup 7))
+ (parallel [(set (match_dup 6)
+ (ior:QI (match_dup 8) (match_dup 9)))
+ (clobber (reg:CC 21))])]
+ ""
+ "
+ operands[3] = operand_subword (operands[1], 0, 1, HImode); /* lo */
+ operands[4] = operand_subword (operands[1], 1, 1, HImode); /* hi */
+ operands[5] = operand_subword (operands[0], 0, 1, HImode); /* lo */
+ operands[6] = operand_subword (operands[0], 1, 1, HImode); /* hi */
+ operands[7] = gen_reg_rtx (QImode); /* lo << count */
+ operands[8] = gen_reg_rtx (QImode); /* hi << count */
+ operands[9] = gen_reg_rtx (QImode); /* lo >> (32 - count) */
+ operands[10] = gen_reg_rtx (QImode); /* 32 - count */
+ ")
+
+; This should do all the dirty work with define_split
+(define_expand "lshrhi3"
+ [(parallel [(set (match_operand:HI 0 "reg_operand" "")
+ (lshiftrt:HI (match_operand:HI 1 "src_operand" "")
+ (match_operand:QI 2 "src_operand" "")))
+ (clobber (reg:CC 21))])]
+ ""
+ "if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) >= 32)
+ {
+ rtx op0hi = operand_subword (operands[0], 1, 0, HImode);
+ rtx op0lo = operand_subword (operands[0], 0, 0, HImode);
+ rtx op1hi = operand_subword (operands[1], 1, 0, HImode);
+ rtx count = GEN_INT ((INTVAL (operands[2]) - 32));
+
+ if (INTVAL (count))
+ emit_insn (gen_lshrqi3 (op0lo, op1hi, count));
+ else
+ emit_insn (gen_movqi (op0lo, op1hi));
+ emit_insn (gen_movqi (op0hi, const0_rtx));
+ DONE;
+ }
+ if (! REG_P (operands[1]))
+ operands[1] = force_reg (HImode, operands[1]);
+ emit_insn (gen_lshrhi3_reg (operands[0], operands[1], operands[2]));
+ DONE;")
+
+; %0.hi = %1.hi >> %2
+; %0.lo = (%1.lo >> %2 ) | (%1.hi << (32 - %2))
+; This algorithm should work for shift counts greater than 32
+(define_expand "lshrhi3_reg"
+ [(use (match_operand:HI 1 "reg_operand" ""))
+ (use (match_operand:HI 0 "reg_operand" ""))
+ (parallel [(set (match_dup 11)
+ (neg:QI (match_operand:QI 2 "reg_operand" "")))
+ (clobber (reg:CC_NOOV 21))])
+ /* If the shift count is greater than 32 this will give zero. */
+ (parallel [(set (match_dup 7)
+ (lshiftrt:QI (match_dup 3)
+ (neg:QI (match_dup 11))))
+ (clobber (reg:CC 21))])
+ /* If the shift count is greater than 32 this will give zero. */
+ (parallel [(set (match_dup 8)
+ (lshiftrt:QI (match_dup 4)
+ (neg:QI (match_dup 11))))
+ (clobber (reg:CC 21))])
+ (parallel [(set (match_dup 10)
+ (plus:QI (match_dup 11) (const_int 32)))
+ (clobber (reg:CC_NOOV 21))])
+ /* If the shift count is greater than 32 this will do an arithmetic
+ right shift. However, we need a logical right shift. */
+ (parallel [(set (match_dup 9)
+ (ashift:QI (match_dup 4) (unspec [(match_dup 10)] 3)))
+ (clobber (reg:CC 21))])
+ (set (match_dup 6) (match_dup 8))
+ (parallel [(set (match_dup 5)
+ (ior:QI (match_dup 7) (match_dup 9)))
+ (clobber (reg:CC 21))])]
+ ""
+ "
+ operands[3] = operand_subword (operands[1], 0, 1, HImode); /* lo */
+ operands[4] = operand_subword (operands[1], 1, 1, HImode); /* hi */
+ operands[5] = operand_subword (operands[0], 0, 1, HImode); /* lo */
+ operands[6] = operand_subword (operands[0], 1, 1, HImode); /* hi */
+ operands[7] = gen_reg_rtx (QImode); /* lo >> count */
+ operands[8] = gen_reg_rtx (QImode); /* hi >> count */
+ operands[9] = gen_reg_rtx (QImode); /* hi << (32 - count) */
+ operands[10] = gen_reg_rtx (QImode); /* 32 - count */
+ operands[11] = gen_reg_rtx (QImode); /* -count */
+ ")
+
+; This should do all the dirty work with define_split
+(define_expand "ashrhi3"
+ [(parallel [(set (match_operand:HI 0 "reg_operand" "")
+ (ashiftrt:HI (match_operand:HI 1 "src_operand" "")
+ (match_operand:QI 2 "src_operand" "")))
+ (clobber (reg:CC 21))])]
+ ""
+ "if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) >= 32)
+ {
+ rtx op0hi = operand_subword (operands[0], 1, 0, HImode);
+ rtx op0lo = operand_subword (operands[0], 0, 0, HImode);
+ rtx op1hi = operand_subword (operands[1], 1, 0, HImode);
+ rtx count = GEN_INT ((INTVAL (operands[2]) - 32));
+
+ if (INTVAL (count))
+ emit_insn (gen_ashrqi3 (op0lo, op1hi, count));
+ else
+ emit_insn (gen_movqi (op0lo, op1hi));
+ emit_insn (gen_ashrqi3 (op0hi, op1hi, GEN_INT (31)));
+ DONE;
+ }
+ if (! REG_P (operands[1]))
+ operands[1] = force_reg (HImode, operands[1]);
+ emit_insn (gen_ashrhi3_reg (operands[0], operands[1], operands[2]));
+ DONE;")
+
+; %0.hi = %1.hi >> %2
+; %0.lo = (%1.lo >> %2 ) | (%1.hi << (32 - %2))
+; This algorithm should work for shift counts greater than 32
+(define_expand "ashrhi3_reg"
+ [(use (match_operand:HI 1 "reg_operand" ""))
+ (use (match_operand:HI 0 "reg_operand" ""))
+ (parallel [(set (match_dup 11)
+ (neg:QI (match_operand:QI 2 "reg_operand" "")))
+ (clobber (reg:CC_NOOV 21))])
+ /* If the shift count is greater than 32 this will give zero. */
+ (parallel [(set (match_dup 7)
+ (lshiftrt:QI (match_dup 3)
+ (neg:QI (match_dup 11))))
+ (clobber (reg:CC 21))])
+ /* If the shift count is greater than 32 this will give zero. */
+ (parallel [(set (match_dup 8)
+ (ashiftrt:QI (match_dup 4)
+ (neg:QI (match_dup 11))))
+ (clobber (reg:CC 21))])
+ (parallel [(set (match_dup 10)
+ (plus:QI (match_dup 11) (const_int 32)))
+ (clobber (reg:CC_NOOV 21))])
+ /* If the shift count is greater than 32 this will do an arithmetic
+ right shift. */
+ (parallel [(set (match_dup 9)
+ (ashift:QI (match_dup 4) (match_dup 10)))
+ (clobber (reg:CC 21))])
+ (set (match_dup 6) (match_dup 8))
+ (parallel [(set (match_dup 5)
+ (ior:QI (match_dup 7) (match_dup 9)))
+ (clobber (reg:CC 21))])]
+ ""
+ "
+ operands[3] = operand_subword (operands[1], 0, 1, HImode); /* lo */
+ operands[4] = operand_subword (operands[1], 1, 1, HImode); /* hi */
+ operands[5] = operand_subword (operands[0], 0, 1, HImode); /* lo */
+ operands[6] = operand_subword (operands[0], 1, 1, HImode); /* hi */
+ operands[7] = gen_reg_rtx (QImode); /* lo >> count */
+ operands[8] = gen_reg_rtx (QImode); /* hi >> count */
+ operands[9] = gen_reg_rtx (QImode); /* hi << (32 - count) */
+ operands[10] = gen_reg_rtx (QImode); /* 32 - count */
+ operands[11] = gen_reg_rtx (QImode); /* -count */
+ ")
+
+(define_expand "cmphi"
+ [(set (reg:CC 21)
+ (compare:CC (match_operand:HI 0 "src_operand" "")
+ (match_operand:HI 1 "src_operand" "")))]
+ ""
+ "legitimize_operands (COMPARE, operands, HImode);
+ c4x_compare_op0 = operands[0];
+ c4x_compare_op1 = operands[1];
+ DONE;")
+
+; This works only before reload because we need 2 extra registers.
+; Use unspec to avoid recursive split.
+(define_split
+ [(set (reg:CC 21)
+ (compare:CC (match_operand:HI 0 "src_operand" "")
+ (match_operand:HI 1 "src_operand" "")))]
+ "! reload_completed"
+ [(parallel [(set (reg:CC 21)
+ (unspec [(compare:CC (match_dup 0)
+ (match_dup 1))] 4))
+ (clobber (match_scratch:QI 2 ""))
+ (clobber (match_scratch:QI 3 ""))])]
+ "")
+
+(define_split
+ [(set (reg:CC_NOOV 21)
+ (compare:CC_NOOV (match_operand:HI 0 "src_operand" "")
+ (match_operand:HI 1 "src_operand" "")))]
+ "! reload_completed"
+ [(parallel [(set (reg:CC_NOOV 21)
+ (unspec [(compare:CC_NOOV (match_dup 0)
+ (match_dup 1))] 4))
+ (clobber (match_scratch:QI 2 ""))
+ (clobber (match_scratch:QI 3 ""))])]
+ "")
+
+; This is normally not used. The define splits above are used first.
+(define_insn "*cmphi"
+ [(set (reg:CC 21)
+ (compare:CC (match_operand:HI 0 "src_operand" "rR,rS<>")
+ (match_operand:HI 1 "src_operand" "R,rS<>")))]
+ "valid_operands (COMPARE, operands, HImode)"
+ "*
+ {
+ int use_ir1 = (reg_operand (operands[0], HImode)
+ && REG_P (operands[0])
+ && REGNO (operands[0]) == IR1_REGNO)
+ || (reg_operand (operands[1], HImode)
+ && REG_P (operands[1])
+ && REGNO (operands[1]) == IR1_REGNO);
+
+ if (use_ir1)
+ output_asm_insn (\"push\\tir1\", operands);
+ else
+ output_asm_insn (\"push\\tbk\", operands);
+ output_asm_insn (\"push\\tr0\", operands);
+ output_asm_insn (\"subi3\\t%1,%0,r0\", operands);
+ if (use_ir1)
+ {
+ output_asm_insn (\"ldiu\\tst,ir1\", operands);
+ output_asm_insn (\"or\\t07bh,ir1\", operands);
+ }
+ else
+ {
+ output_asm_insn (\"ldiu\\tst,bk\", operands);
+ output_asm_insn (\"or\\t07bh,bk\", operands);
+ }
+ output_asm_insn (\"subb3\\t%O1,%O0,r0\", operands);
+ if (use_ir1)
+ output_asm_insn (\"and3\\tir1,st,ir1\", operands);
+ else
+ output_asm_insn (\"and3\\tbk,st,bk\", operands);
+ output_asm_insn (\"pop\\tr0\", operands);
+ if (use_ir1)
+ {
+ output_asm_insn (\"ldiu\\tir1,st\", operands);
+ output_asm_insn (\"pop\\tir1\", operands);
+ }
+ else
+ {
+ output_asm_insn (\"ldiu\\tbk,st\", operands);
+ output_asm_insn (\"pop\\tbk\", operands);
+ }
+ return \"\";
+ }"
+ [(set_attr "type" "multi")])
+
+(define_insn "*cmphi_noov"
+ [(set (reg:CC_NOOV 21)
+ (compare:CC_NOOV (match_operand:HI 0 "src_operand" "rR,rS<>")
+ (match_operand:HI 1 "src_operand" "R,rS<>")))]
+ "valid_operands (COMPARE, operands, HImode)"
+ "*
+ {
+ int use_ir1 = (reg_operand (operands[0], HImode)
+ && REG_P (operands[0])
+ && REGNO (operands[0]) == IR1_REGNO)
+ || (reg_operand (operands[1], HImode)
+ && REG_P (operands[1])
+ && REGNO (operands[1]) == IR1_REGNO);
+
+ if (use_ir1)
+ output_asm_insn (\"push\\tir1\", operands);
+ else
+ output_asm_insn (\"push\\tbk\", operands);
+ output_asm_insn (\"push\\tr0\", operands);
+ output_asm_insn (\"subi3\\t%1,%0,r0\", operands);
+ if (use_ir1)
+ {
+ output_asm_insn (\"ldiu\\tst,ir1\", operands);
+ output_asm_insn (\"or\\t07bh,ir1\", operands);
+ }
+ else
+ {
+ output_asm_insn (\"ldiu\\tst,bk\", operands);
+ output_asm_insn (\"or\\t07bh,bk\", operands);
+ }
+ output_asm_insn (\"subb3\\t%O1,%O0,r0\", operands);
+ if (use_ir1)
+ output_asm_insn (\"and3\\tir1,st,ir1\", operands);
+ else
+ output_asm_insn (\"and3\\tbk,st,bk\", operands);
+ output_asm_insn (\"pop\\tr0\", operands);
+ if (use_ir1)
+ {
+ output_asm_insn (\"ldiu\\tir1,st\", operands);
+ output_asm_insn (\"pop\\tir1\", operands);
+ }
+ else
+ {
+ output_asm_insn (\"ldiu\\tbk,st\", operands);
+ output_asm_insn (\"pop\\tbk\", operands);
+ }
+ return \"\";
+ }"
+ [(set_attr "type" "multi")])
+
+
+(define_insn "cmphi_cc"
+ [(set (reg:CC 21)
+ (unspec [(compare:CC (match_operand:HI 0 "src_operand" "rR,rS<>")
+ (match_operand:HI 1 "src_operand" "R,rS<>"))] 4))
+ (clobber (match_scratch:QI 2 "=&d,&d"))
+ (clobber (match_scratch:QI 3 "=&c,&c"))]
+ "valid_operands (COMPARE, operands, HImode)"
+ "*
+ output_asm_insn (\"subi3\\t%1,%0,%2\", operands);
+ output_asm_insn (\"ldiu\\tst,%3\", operands);
+ output_asm_insn (\"or\\t07bh,%3\", operands);
+ output_asm_insn (\"subb3\\t%O1,%O0,%2\", operands);
+ output_asm_insn (\"and\\t%3,st\", operands);
+ return \"\";"
+ [(set_attr "type" "multi")])
+
+(define_insn "cmphi_cc_noov"
+ [(set (reg:CC_NOOV 21)
+ (unspec [(compare:CC_NOOV (match_operand:HI 0 "src_operand" "rR,rS<>")
+ (match_operand:HI 1 "src_operand" "R,rS<>"))] 4))
+ (clobber (match_scratch:QI 2 "=&d,&d"))
+ (clobber (match_scratch:QI 3 "=&c,&c"))]
+ "valid_operands (COMPARE, operands, HImode)"
+ "*
+ output_asm_insn (\"subi3\\t%1,%0,%2\", operands);
+ output_asm_insn (\"ldiu\\tst,%3\", operands);
+ output_asm_insn (\"or\\t07bh,%3\", operands);
+ output_asm_insn (\"subb3\\t%O1,%O0,%2\", operands);
+ output_asm_insn (\"and\\t%3,st\", operands);
+ return \"\";"
+ [(set_attr "type" "multi")])
+
+(define_expand "mulhi3"
+ [(parallel [(set (match_operand:HI 0 "reg_operand" "")
+ (mult:HI (match_operand:HI 1 "src_operand" "")
+ (match_operand:HI 2 "src_operand" "")))
+ (clobber (reg:CC 21))])]
+ ""
+ "c4x_emit_libcall3 (MULHI3_LIBCALL, MULT, HImode, operands);
+ DONE;")
+
+(define_expand "udivhi3"
+ [(parallel [(set (match_operand:HI 0 "reg_operand" "")
+ (udiv:HI (match_operand:HI 1 "src_operand" "")
+ (match_operand:HI 2 "src_operand" "")))
+ (clobber (reg:CC 21))])]
+ ""
+ "c4x_emit_libcall3 (UDIVHI3_LIBCALL, UDIV, HImode, operands);
+ DONE;")
+
+(define_expand "divhi3"
+ [(parallel [(set (match_operand:HI 0 "reg_operand" "")
+ (div:HI (match_operand:HI 1 "src_operand" "")
+ (match_operand:HI 2 "src_operand" "")))
+ (clobber (reg:CC 21))])]
+ ""
+ "c4x_emit_libcall3 (DIVHI3_LIBCALL, DIV, HImode, operands);
+ DONE;")
+
+(define_expand "umodhi3"
+ [(parallel [(set (match_operand:HI 0 "reg_operand" "")
+ (umod:HI (match_operand:HI 1 "src_operand" "")
+ (match_operand:HI 2 "src_operand" "")))
+ (clobber (reg:CC 21))])]
+ ""
+ "c4x_emit_libcall3 (UMODHI3_LIBCALL, UMOD, HImode, operands);
+ DONE;")
+
+(define_expand "modhi3"
+ [(parallel [(set (match_operand:HI 0 "reg_operand" "")
+ (mod:HI (match_operand:HI 1 "src_operand" "")
+ (match_operand:HI 2 "src_operand" "")))
+ (clobber (reg:CC 21))])]
+ ""
+ "c4x_emit_libcall3 (MODHI3_LIBCALL, MOD, HImode, operands);
+ DONE;")
+
+;
+; PEEPHOLES
+;
+
+; dbCC peepholes
+;
+; Turns
+; loop:
+; [ ... ]
+; bCC label ; abnormal loop termination
+; dbu aN, loop ; normal loop termination
+;
+; Into
+; loop:
+; [ ... ]
+; dbCC aN, loop
+; bCC label
+;
+; Which moves the bCC condition outside the inner loop for free.
+;
+(define_peephole
+ [(set (pc) (if_then_else (match_operator 3 "comparison_operator"
+ [(reg:CC 21) (const_int 0)])
+ (label_ref (match_operand 2 "" ""))
+ (pc)))
+ (parallel
+ [(set (pc)
+ (if_then_else
+ (ge (plus:QI (match_operand:QI 0 "addr_reg_operand" "+a")
+ (const_int -1))
+ (const_int 0))
+ (label_ref (match_operand 1 "" ""))
+ (pc)))
+ (set (match_dup 0)
+ (plus:QI (match_dup 0)
+ (const_int -1)))
+ (clobber (reg:CC_NOOV 21))])]
+ "! c4x_label_conflict (insn, operands[2], operands[1])"
+ "db%I3\\t%0,%l1\\n\\tb%3\\t%l2"
+ [(set_attr "type" "multi")])
+
+(define_peephole
+ [(set (pc) (if_then_else (match_operator 3 "comparison_operator"
+ [(reg:CC 21) (const_int 0)])
+ (label_ref (match_operand 2 "" ""))
+ (pc)))
+ (parallel
+ [(set (pc)
+ (if_then_else
+ (ne (match_operand:QI 0 "addr_reg_operand" "+a")
+ (const_int 0))
+ (label_ref (match_operand 1 "" ""))
+ (pc)))
+ (set (match_dup 0)
+ (plus:QI (match_dup 0)
+ (const_int -1)))
+ (clobber (reg:CC_NOOV 21))])]
+ "! c4x_label_conflict (insn, operands[2], operands[1])"
+ "db%I3\\t%0,%l1\\n\\tb%3\\t%l2"
+ [(set_attr "type" "multi")])
+
+;
+; Peepholes to convert 'call label; rets' into jump label
+;
+(define_peephole
+ [(parallel [(call (mem:QI (match_operand:QI 0 "call_address_operand" ""))
+ (match_operand:QI 1 "general_operand" ""))
+ (clobber (reg:QI 31))])
+ (return)]
+ "c4x_null_epilogue_p ()"
+ "*
+ if (REG_P (operands[0]))
+ return \"bu%#\\t%C0\";
+ else
+ return \"br%#\\t%C0\";"
+ [(set_attr "type" "jump")])
+
+(define_peephole
+ [(parallel [(set (match_operand 0 "" "")
+ (call (mem:QI (match_operand:QI 1 "call_address_operand" ""))
+ (match_operand:QI 2 "general_operand" "")))
+ (clobber (reg:QI 31))])
+ (return)]
+ "c4x_null_epilogue_p ()"
+ "*
+ if (REG_P (operands[1]))
+ return \"bu%#\\t%C1\";
+ else
+ return \"br%#\\t%C1\";"
+ [(set_attr "type" "jump")])
+
+;
+; Peepholes for parallel instructions
+;
+(define_peephole
+ [(set (match_operand:QI 0 "ext_low_reg_operand" "")
+ (match_operand:QI 1 "par_ind_operand" ""))
+ (set (match_operand:QI 2 "ext_low_reg_operand" "")
+ (match_operand:QI 3 "par_ind_operand" ""))]
+ "(REGNO (operands[0]) != REGNO (operands[2]))
+ && ! c4x_address_conflict (operands[1], operands[3], 0, 0)"
+ "ldi1\\t%1,%0\\n||\\tldi2\\t%3,%2")
+
+; load occurs before store if 1 and 2 point to same address
+(define_peephole
+ [(set (match_operand:QI 0 "ext_low_reg_operand" "")
+ (match_operand:QI 1 "par_ind_operand" ""))
+ (set (match_operand:QI 2 "par_ind_operand" "")
+ (match_operand:QI 3 "ext_low_reg_operand" ""))]
+ "(REGNO (operands[0]) != REGNO (operands[3]))
+ && ! c4x_address_conflict (operands[1], operands[2], 0, 1)"
+ "ldi\\t%1,%0\\n||\\tsti\\t%3,%2")
+
+; load occurs before store if 0 and 3 point to same address
+(define_peephole
+ [(set (match_operand:QI 0 "par_ind_operand" "")
+ (match_operand:QI 1 "ext_low_reg_operand" ""))
+ (set (match_operand:QI 2 "ext_low_reg_operand" "")
+ (match_operand:QI 3 "par_ind_operand" ""))]
+ "(REGNO (operands[1]) != REGNO (operands[2]))
+ && ! c4x_address_conflict (operands[0], operands[3], 1, 0)"
+ "ldi\\t%3,%2\\n||\\tsti\\t%1,%0")
+
+(define_peephole
+ [(set (match_operand:QI 0 "par_ind_operand" "")
+ (match_operand:QI 1 "ext_low_reg_operand" ""))
+ (set (match_operand:QI 2 "par_ind_operand" "")
+ (match_operand:QI 3 "ext_low_reg_operand" ""))]
+ "! c4x_address_conflict (operands[0], operands[2], 1, 1)"
+ "sti\\t%1,%0\\n||\\tsti\\t%3,%2")
+
+; This peephole should be unnecessary with my patches to flow.c
+; for better autoincrement detection
+(define_peephole
+ [(set (match_operand:QF 0 "ext_low_reg_operand" "")
+ (mem:QF (match_operand:QI 1 "addr_reg_operand" "")))
+ (set (match_operand:QF 2 "ext_low_reg_operand" "")
+ (mem:QF (plus:QI (match_dup 1) (const_int 1))))
+ (parallel [(set (match_dup 1) (plus:QI (match_dup 1) (const_int 2)))
+ (clobber (reg:CC_NOOV 21))])]
+ ""
+ "ldf\\t*%1++,%0\\n\\tldf\\t*%1++,%2")
+
+(define_peephole
+ [(set (match_operand:QF 0 "ext_low_reg_operand" "")
+ (match_operand:QF 1 "par_ind_operand" ""))
+ (set (match_operand:QF 2 "ext_low_reg_operand" "")
+ (match_operand:QF 3 "par_ind_operand" ""))]
+ "(REGNO (operands[0]) != REGNO (operands[2]))
+ && ! c4x_address_conflict (operands[1], operands[3], 0, 1)"
+ "ldf1\\t%1,%0\\n||\\tldf2\\t%3,%2")
+
+; This peephole should be unnecessary with my patches to flow.c
+; for better autoincrement detection
+(define_peephole
+ [(set (mem:QF (match_operand:QI 0 "addr_reg_operand" ""))
+ (match_operand:QF 1 "ext_low_reg_operand" ""))
+ (set (mem:QF (plus:QI (match_dup 0) (const_int 1)))
+ (match_operand:QF 2 "ext_low_reg_operand" ""))
+ (parallel [(set (match_dup 0) (plus:QI (match_dup 0) (const_int 2)))
+ (clobber (reg:CC_NOOV 21))])]
+ ""
+ "stf\\t%1,*%0++\\n\\tstf\\t%2,*%0++")
+
+(define_peephole
+ [(set (match_operand:QF 0 "ext_low_reg_operand" "")
+ (match_operand:QF 1 "par_ind_operand" ""))
+ (set (match_operand:QF 2 "par_ind_operand" "")
+ (match_operand:QF 3 "ext_low_reg_operand" ""))]
+ "(REGNO (operands[0]) != REGNO (operands[3]))"
+ "ldf\\t%1,%0\\n||\\tstf\\t%3,%2")
+
+(define_peephole
+ [(set (match_operand:QF 0 "par_ind_operand" "")
+ (match_operand:QF 1 "ext_low_reg_operand" ""))
+ (set (match_operand:QF 2 "ext_low_reg_operand" "")
+ (match_operand:QF 3 "par_ind_operand" ""))]
+ "! c4x_address_conflict (operands[0], operands[3], 1, 1)"
+ "ldf\\t%3,%2\\n||\\tstf\\t%1,%0")
+
+(define_peephole
+ [(set (match_operand:QF 0 "par_ind_operand" "")
+ (match_operand:QF 1 "ext_low_reg_operand" ""))
+ (set (match_operand:QF 2 "par_ind_operand" "")
+ (match_operand:QF 3 "ext_low_reg_operand" ""))]
+ "! c4x_address_conflict (operands[0], operands[2], 1, 1)"
+ "stf1\\t%1,%0\\n||\\tstf2\\t%3,%2")
+
+(define_peephole
+ [(parallel [(set (reg:CC_NOOV 21)
+ (compare:CC_NOOV (abs:QF (match_operand:QF 1 "par_ind_operand" ""))
+ (match_operand:QF 2 "fp_zero_operand" "")))
+ (set (match_operand:QF 0 "ext_low_reg_operand" "")
+ (abs:QF (match_dup 1)))])
+ (set (match_operand:QF 3 "par_ind_operand" "")
+ (match_operand:QF 4 "ext_low_reg_operand" ""))]
+ "(REGNO (operands[0]) != REGNO (operands[4]))"
+ "absf\\t%1,%0\\n||\\tstf\\t%4,%3")
+
+(define_peephole
+ [(parallel [(set (match_operand:QF 0 "ext_low_reg_operand" "")
+ (abs:QF (match_operand:QF 1 "par_ind_operand" "")))
+ (clobber (reg:CC_NOOV 21))])
+ (set (match_operand:QF 2 "par_ind_operand" "")
+ (match_operand:QF 3 "ext_low_reg_operand" ""))]
+ "(REGNO (operands[0]) != REGNO (operands[3]))"
+ "absf\\t%1,%0\\n||\\tstf\\t%3,%2")
+
+(define_peephole
+ [(parallel [(set (reg:CC_NOOV 21)
+ (compare:CC_NOOV (abs:QI (match_operand:QI 1 "par_ind_operand" ""))
+ (const_int 0)))
+ (set (match_operand:QI 0 "ext_low_reg_operand" "")
+ (abs:QI (match_dup 1)))])
+ (set (match_operand:QI 2 "par_ind_operand" "")
+ (match_operand:QI 3 "ext_low_reg_operand" ""))]
+ "(REGNO (operands[0]) != REGNO (operands[3]))"
+ "absi\\t%1,%0\\n||\\tsti\\t%3,%2")
+
+(define_peephole
+ [(parallel [(set (match_operand:QI 0 "ext_low_reg_operand" "")
+ (abs:QI (match_operand:QI 1 "par_ind_operand" "")))
+ (clobber (reg:CC_NOOV 21))])
+ (set (match_operand:QI 2 "par_ind_operand" "")
+ (match_operand:QI 3 "ext_low_reg_operand" ""))]
+ "(REGNO (operands[0]) != REGNO (operands[3]))"
+ "absi\\t%1,%0\\n||\\tsti\\t%3,%2")
+
+(define_peephole
+ [(parallel [(set (reg:CC_NOOV 21)
+ (compare:CC_NOOV (plus:QI (match_operand:QI 1 "ext_low_reg_operand" "")
+ (match_operand:QI 2 "par_ind_operand" ""))
+ (const_int 0)))
+ (set (match_operand:QI 0 "ext_low_reg_operand" "")
+ (plus:QI (match_dup 1) (match_dup 2)))])
+ (set (match_operand:QI 3 "par_ind_operand" "")
+ (match_operand:QI 4 "ext_low_reg_operand" ""))]
+ "(REGNO (operands[0]) != REGNO (operands[4]))"
+ "addi3\\t%2,%1,%0\\n||\\tsti\\t%4,%3")
+
+(define_peephole
+ [(parallel [(set (reg:CC_NOOV 21)
+ (compare:CC_NOOV (plus:QI (match_operand:QI 1 "par_ind_operand" "")
+ (match_operand:QI 2 "ext_low_reg_operand" ""))
+ (const_int 0)))
+ (set (match_operand:QI 0 "ext_low_reg_operand" "")
+ (plus:QI (match_dup 1) (match_dup 2)))])
+ (set (match_operand:QI 3 "par_ind_operand" "")
+ (match_operand:QI 4 "ext_low_reg_operand" ""))]
+ "(REGNO (operands[0]) != REGNO (operands[4]))"
+ "addi3\\t%2,%1,%0\\n||\\tsti\\t%4,%3")
+
+(define_peephole
+ [(parallel [(set (match_operand:QI 0 "ext_low_reg_operand" "")
+ (plus:QI (match_operand:QI 1 "ext_low_reg_operand" "")
+ (match_operand:QI 2 "par_ind_operand" "")))
+ (clobber (reg:CC_NOOV 21))])
+ (set (match_operand:QI 3 "par_ind_operand" "")
+ (match_operand:QI 4 "ext_low_reg_operand" ""))]
+ "(REGNO (operands[0]) != REGNO (operands[4]))"
+ "addi3\\t%2,%1,%0\\n||\\tsti\\t%4,%3")
+
+(define_peephole
+ [(parallel [(set (match_operand:QI 0 "ext_low_reg_operand" "")
+ (plus:QI (match_operand:QI 1 "par_ind_operand" "")
+ (match_operand:QI 2 "ext_low_reg_operand" "")))
+ (clobber (reg:CC_NOOV 21))])
+ (set (match_operand:QI 3 "par_ind_operand" "")
+ (match_operand:QI 4 "ext_low_reg_operand" ""))]
+ "(REGNO (operands[0]) != REGNO (operands[4]))"
+ "addi3\\t%2,%1,%0\\n||\\tsti\\t%4,%3")
+
+(define_peephole
+ [(parallel [(set (reg:CC_NOOV 21)
+ (compare:CC_NOOV (plus:QF (match_operand:QF 1 "ext_low_reg_operand" "")
+ (match_operand:QF 2 "par_ind_operand" ""))
+ (match_operand:QF 3 "fp_zero_operand" "")))
+ (set (match_operand:QF 0 "ext_low_reg_operand" "")
+ (plus:QF (match_dup 1) (match_dup 2)))])
+ (set (match_operand:QF 4 "par_ind_operand" "")
+ (match_operand:QF 5 "ext_low_reg_operand" ""))]
+ "(REGNO (operands[0]) != REGNO (operands[5]))"
+ "addf3\\t%2,%1,%0\\n||\\tstf\\t%5,%4")
+
+(define_peephole
+ [(parallel [(set (reg:CC_NOOV 21)
+ (compare:CC_NOOV (plus:QF (match_operand:QF 1 "par_ind_operand" "")
+ (match_operand:QF 2 "ext_low_reg_operand" ""))
+ (match_operand:QF 3 "fp_zero_operand" "")))
+ (set (match_operand:QF 0 "ext_low_reg_operand" "")
+ (plus:QF (match_dup 1) (match_dup 2)))])
+ (set (match_operand:QF 4 "par_ind_operand" "")
+ (match_operand:QF 5 "ext_low_reg_operand" ""))]
+ "(REGNO (operands[0]) != REGNO (operands[5]))"
+ "addf3\\t%2,%1,%0\\n||\\tstf\\t%5,%4")
+
+(define_peephole
+ [(parallel [(set (match_operand:QF 0 "ext_low_reg_operand" "")
+ (plus:QF (match_operand:QF 1 "ext_low_reg_operand" "")
+ (match_operand:QF 2 "par_ind_operand" "")))
+ (clobber (reg:CC_NOOV 21))])
+ (set (match_operand:QF 3 "par_ind_operand" "")
+ (match_operand:QF 4 "ext_low_reg_operand" ""))]
+ "(REGNO (operands[0]) != REGNO (operands[4]))"
+ "addf3\\t%2,%1,%0\\n||\\tstf\\t%4,%3")
+
+(define_peephole
+ [(parallel [(set (match_operand:QF 0 "ext_low_reg_operand" "")
+ (plus:QF (match_operand:QF 1 "par_ind_operand" "")
+ (match_operand:QF 2 "ext_low_reg_operand" "")))
+ (clobber (reg:CC_NOOV 21))])
+ (set (match_operand:QF 3 "par_ind_operand" "")
+ (match_operand:QF 4 "ext_low_reg_operand" ""))]
+ "(REGNO (operands[0]) != REGNO (operands[4]))"
+ "addf3\\t%2,%1,%0\\n||\\tstf\\t%4,%3")
+
+(define_peephole
+ [(parallel [(set (reg:CC 21)
+ (compare:CC (and:QI (match_operand:QI 1 "ext_low_reg_operand" "")
+ (match_operand:QI 2 "par_ind_operand" ""))
+ (const_int 0)))
+ (set (match_operand:QI 0 "ext_low_reg_operand" "")
+ (and:QI (match_dup 1) (match_dup 2)))])
+ (set (match_operand:QI 3 "par_ind_operand" "")
+ (match_operand:QI 4 "ext_low_reg_operand" ""))]
+ "(REGNO (operands[0]) != REGNO (operands[4]))"
+ "and3\\t%2,%1,%0\\n||\\tsti\\t%4,%3")
+
+(define_peephole
+ [(parallel [(set (reg:CC 21)
+ (compare:CC (and:QI (match_operand:QI 1 "par_ind_operand" "")
+ (match_operand:QI 2 "ext_low_reg_operand" ""))
+ (const_int 0)))
+ (set (match_operand:QI 0 "ext_low_reg_operand" "")
+ (and:QI (match_dup 1) (match_dup 2)))])
+ (set (match_operand:QI 3 "par_ind_operand" "")
+ (match_operand:QI 4 "ext_low_reg_operand" ""))]
+ "(REGNO (operands[0]) != REGNO (operands[4]))"
+ "and3\\t%2,%1,%0\\n||\\tsti\\t%4,%3")
+
+(define_peephole
+ [(parallel [(set (match_operand:QI 0 "ext_low_reg_operand" "")
+ (and:QI (match_operand:QI 1 "ext_low_reg_operand" "")
+ (match_operand:QI 2 "par_ind_operand" "")))
+ (clobber (reg:CC 21))])
+ (set (match_operand:QI 3 "par_ind_operand" "")
+ (match_operand:QI 4 "ext_low_reg_operand" ""))]
+ "(REGNO (operands[0]) != REGNO (operands[4]))"
+ "and3\\t%2,%1,%0\\n||\\tsti\\t%4,%3")
+
+(define_peephole
+ [(parallel [(set (match_operand:QI 0 "ext_low_reg_operand" "")
+ (and:QI (match_operand:QI 1 "par_ind_operand" "")
+ (match_operand:QI 2 "ext_low_reg_operand" "")))
+ (clobber (reg:CC 21))])
+ (set (match_operand:QI 3 "par_ind_operand" "")
+ (match_operand:QI 4 "ext_low_reg_operand" ""))]
+ "(REGNO (operands[0]) != REGNO (operands[4]))"
+ "and3\\t%2,%1,%0\\n||\\tsti\\t%4,%3")
+
+(define_peephole
+ [(parallel [(set (match_operand:QI 0 "ext_low_reg_operand" "")
+ (ashift:QI (match_operand:QI 1 "par_ind_operand" "")
+ (match_operand:QI 2 "ext_low_reg_operand" "")))
+ (clobber (reg:CC 21))])
+ (set (match_operand:QI 3 "par_ind_operand" "")
+ (match_operand:QI 4 "ext_low_reg_operand" ""))]
+ "(REGNO (operands[0]) != REGNO (operands[4]))"
+ "ash3\\t%2,%1,%0\\n||\\tsti\\t%4,%3")
+
+(define_peephole
+ [(parallel [(set (match_operand:QI 0 "ext_low_reg_operand" "")
+ (ashiftrt:QI (match_operand:QI 1 "par_ind_operand" "")
+ (neg:QI (match_operand:QI 2 "ext_low_reg_operand" ""))))
+ (clobber (reg:CC 21))])
+ (set (match_operand:QI 3 "par_ind_operand" "")
+ (match_operand:QI 4 "ext_low_reg_operand" ""))]
+ "(REGNO (operands[0]) != REGNO (operands[4]))"
+ "ash3\\t%2,%1,%0\\n||\\tsti\\t%4,%3")
+
+(define_peephole
+ [(parallel [(set (reg:CC 21)
+ (compare:CC (fix:QI (match_operand:QF 1 "par_ind_operand" ""))
+ (const_int 0)))
+ (set (match_operand:QI 0 "ext_low_reg_operand" "")
+ (fix:QI (match_dup 1)))])
+ (set (match_operand:QI 2 "par_ind_operand" "")
+ (match_operand:QI 3 "ext_low_reg_operand" ""))]
+ "(REGNO (operands[0]) != REGNO (operands[3]))"
+ "fix\\t%1,%0\\n||\\tsti\\t%3,%2")
+
+(define_peephole
+ [(parallel [(set (match_operand:QI 0 "ext_low_reg_operand" "")
+ (fix:QI (match_operand:QF 1 "par_ind_operand" "")))
+ (clobber (reg:CC 21))])
+ (set (match_operand:QI 2 "par_ind_operand" "")
+ (match_operand:QI 3 "ext_low_reg_operand" ""))]
+ "(REGNO (operands[0]) != REGNO (operands[3]))"
+ "fix\\t%1,%0\\n||\\tsti\\t%3,%2")
+
+(define_peephole
+ [(parallel [(set (reg:CC 21)
+ (compare:CC (float:QF (match_operand:QI 1 "par_ind_operand" ""))
+ (match_operand:QF 2 "fp_zero_operand" "")))
+ (set (match_operand:QF 0 "ext_low_reg_operand" "")
+ (float:QF (match_dup 1)))])
+ (set (match_operand:QF 3 "par_ind_operand" "")
+ (match_operand:QF 4 "ext_low_reg_operand" ""))]
+ "(REGNO (operands[0]) != REGNO (operands[4]))"
+ "float\\t%1,%0\\n||\\tstf\\t%4,%3")
+
+(define_peephole
+ [(parallel [(set (match_operand:QF 0 "ext_low_reg_operand" "")
+ (float:QF (match_operand:QI 1 "par_ind_operand" "")))
+ (clobber (reg:CC 21))])
+ (set (match_operand:QF 2 "par_ind_operand" "")
+ (match_operand:QF 3 "ext_low_reg_operand" ""))]
+ "(REGNO (operands[0]) != REGNO (operands[3]))"
+ "float\\t%1,%0\\n||\\tstf\\t%3,%2")
+
+(define_peephole
+ [(parallel [(set (reg:CC_NOOV 21)
+ (compare:CC_NOOV (mult:QI (match_operand:QI 1 "ext_low_reg_operand" "")
+ (match_operand:QI 2 "par_ind_operand" ""))
+ (const_int 0)))
+ (set (match_operand:QI 0 "ext_low_reg_operand" "")
+ (mult:QI (match_dup 1) (match_dup 2)))])
+ (set (match_operand:QI 3 "par_ind_operand" "")
+ (match_operand:QI 4 "ext_low_reg_operand" ""))]
+ "(REGNO (operands[0]) != REGNO (operands[4]))"
+ "mpyi3\\t%2,%1,%0\\n||\\tsti\\t%4,%3")
+
+(define_peephole
+ [(parallel [(set (reg:CC_NOOV 21)
+ (compare:CC_NOOV (mult:QI (match_operand:QI 1 "par_ind_operand" "")
+ (match_operand:QI 2 "ext_low_reg_operand" ""))
+ (const_int 0)))
+ (set (match_operand:QI 0 "ext_low_reg_operand" "")
+ (mult:QI (match_dup 1) (match_dup 2)))])
+ (set (match_operand:QI 3 "par_ind_operand" "")
+ (match_operand:QI 4 "ext_low_reg_operand" ""))]
+ "(REGNO (operands[0]) != REGNO (operands[4]))"
+ "mpyi3\\t%2,%1,%0\\n||\\tsti\\t%4,%3")
+
+(define_peephole
+ [(parallel [(set (match_operand:QI 0 "ext_low_reg_operand" "")
+ (mult:QI (match_operand:QI 1 "ext_low_reg_operand" "")
+ (match_operand:QI 2 "par_ind_operand" "")))
+ (clobber (reg:CC 21))])
+ (set (match_operand:QI 3 "par_ind_operand" "")
+ (match_operand:QI 4 "ext_low_reg_operand" ""))]
+ "(REGNO (operands[0]) != REGNO (operands[4]))"
+ "mpyi3\\t%2,%1,%0\\n||\\tsti\\t%4,%3")
+
+(define_peephole
+ [(parallel [(set (match_operand:QI 0 "ext_low_reg_operand" "")
+ (mult:QI (match_operand:QI 1 "par_ind_operand" "")
+ (match_operand:QI 2 "ext_low_reg_operand" "")))
+ (clobber (reg:CC 21))])
+ (set (match_operand:QI 3 "par_ind_operand" "")
+ (match_operand:QI 4 "ext_low_reg_operand" ""))]
+ "(REGNO (operands[0]) != REGNO (operands[4]))"
+ "mpyi3\\t%2,%1,%0\\n||\\tsti\\t%4,%3")
+
+(define_peephole
+ [(parallel [(set (reg:CC_NOOV 21)
+ (compare:CC_NOOV (mult:QF (match_operand:QF 1 "ext_low_reg_operand" "")
+ (match_operand:QF 2 "par_ind_operand" ""))
+ (match_operand:QF 3 "fp_zero_operand" "")))
+ (set (match_operand:QF 0 "ext_low_reg_operand" "")
+ (mult:QF (match_dup 1) (match_dup 2)))])
+ (set (match_operand:QF 4 "par_ind_operand" "")
+ (match_operand:QF 5 "ext_low_reg_operand" ""))]
+ "(REGNO (operands[0]) != REGNO (operands[5]))"
+ "mpyf3\\t%2,%1,%0\\n||\\tstf\\t%5,%4")
+
+(define_peephole
+ [(parallel [(set (reg:CC_NOOV 21)
+ (compare:CC_NOOV (mult:QF (match_operand:QF 1 "par_ind_operand" "")
+ (match_operand:QF 2 "ext_low_reg_operand" ""))
+ (match_operand:QF 3 "fp_zero_operand" "")))
+ (set (match_operand:QF 0 "ext_low_reg_operand" "")
+ (mult:QF (match_dup 1) (match_dup 2)))])
+ (set (match_operand:QF 4 "par_ind_operand" "")
+ (match_operand:QF 5 "ext_low_reg_operand" ""))]
+ "(REGNO (operands[0]) != REGNO (operands[5]))"
+ "mpyf3\\t%2,%1,%0\\n||\\tstf\\t%5,%4")
+
+(define_peephole
+ [(parallel [(set (match_operand:QF 0 "ext_low_reg_operand" "")
+ (mult:QF (match_operand:QF 1 "ext_low_reg_operand" "")
+ (match_operand:QF 2 "par_ind_operand" "")))
+ (clobber (reg:CC 21))])
+ (set (match_operand:QF 3 "par_ind_operand" "")
+ (match_operand:QF 4 "ext_low_reg_operand" ""))]
+ "(REGNO (operands[0]) != REGNO (operands[4]))"
+ "mpyf3\\t%2,%1,%0\\n||\\tstf\\t%4,%3")
+
+(define_peephole
+ [(parallel [(set (match_operand:QF 0 "ext_low_reg_operand" "")
+ (mult:QF (match_operand:QF 1 "par_ind_operand" "")
+ (match_operand:QF 2 "ext_low_reg_operand" "")))
+ (clobber (reg:CC 21))])
+ (set (match_operand:QF 3 "par_ind_operand" "")
+ (match_operand:QF 4 "ext_low_reg_operand" ""))]
+ "(REGNO (operands[0]) != REGNO (operands[4]))"
+ "mpyf3\\t%2,%1,%0\\n||\\tstf\\t%4,%3")
+
+(define_peephole
+ [(parallel [(set (reg:CC_NOOV 21)
+ (compare:CC_NOOV (neg:QF (match_operand:QF 1 "par_ind_operand" ""))
+ (match_operand:QF 2 "fp_zero_operand" "")))
+ (set (match_operand:QF 0 "ext_low_reg_operand" "")
+ (neg:QF (match_dup 1)))])
+ (set (match_operand:QF 3 "par_ind_operand" "")
+ (match_operand:QF 4 "ext_low_reg_operand" ""))]
+ "(REGNO (operands[0]) != REGNO (operands[4]))"
+ "negf\\t%1,%0\\n||\\tstf\\t%4,%3")
+
+(define_peephole
+ [(parallel [(set (match_operand:QF 0 "ext_low_reg_operand" "")
+ (neg:QF (match_operand:QF 1 "par_ind_operand" "")))
+ (clobber (reg:CC_NOOV 21))])
+ (set (match_operand:QF 2 "par_ind_operand" "")
+ (match_operand:QF 3 "ext_low_reg_operand" ""))]
+ "(REGNO (operands[0]) != REGNO (operands[3]))"
+ "negf\\t%1,%0\\n||\\tstf\\t%3,%2")
+
+(define_peephole
+ [(parallel [(set (reg:CC_NOOV 21)
+ (compare:CC_NOOV (neg:QI (match_operand:QI 1 "par_ind_operand" ""))
+ (const_int 0)))
+ (set (match_operand:QI 0 "ext_low_reg_operand" "")
+ (neg:QI (match_dup 1)))])
+ (set (match_operand:QI 2 "par_ind_operand" "")
+ (match_operand:QI 3 "ext_low_reg_operand" ""))]
+ "(REGNO (operands[0]) != REGNO (operands[3]))"
+ "negi\\t%1,%0\\n||\\tsti\\t%3,%2")
+
+(define_peephole
+ [(parallel [(set (match_operand:QI 0 "ext_low_reg_operand" "")
+ (neg:QI (match_operand:QI 1 "par_ind_operand" "")))
+ (clobber (reg:CC_NOOV 21))])
+ (set (match_operand:QI 2 "par_ind_operand" "")
+ (match_operand:QI 3 "ext_low_reg_operand" ""))]
+ "(REGNO (operands[0]) != REGNO (operands[3]))"
+ "negi\\t%1,%0\\n||\\tsti\\t%3,%2")
+
+(define_peephole
+ [(parallel [(set (reg:CC 21)
+ (compare:CC (not:QI (match_operand:QI 1 "par_ind_operand" ""))
+ (const_int 0)))
+ (set (match_operand:QI 0 "ext_low_reg_operand" "")
+ (not:QI (match_dup 1)))])
+ (set (match_operand:QI 2 "par_ind_operand" "")
+ (match_operand:QI 3 "ext_low_reg_operand" ""))]
+ "(REGNO (operands[0]) != REGNO (operands[3]))"
+ "not\\t%1,%0\\n||\\tsti\\t%3,%2")
+
+(define_peephole
+ [(parallel [(set (match_operand:QI 0 "ext_low_reg_operand" "")
+ (not:QI (match_operand:QI 1 "par_ind_operand" "")))
+ (clobber (reg:CC 21))])
+ (set (match_operand:QI 2 "par_ind_operand" "")
+ (match_operand:QI 3 "ext_low_reg_operand" ""))]
+ "(REGNO (operands[0]) != REGNO (operands[3]))"
+ "not\\t%1,%0\\n||\\tsti\\t%3,%2")
+
+(define_peephole
+ [(parallel [(set (reg:CC 21)
+ (compare:CC (ior:QI (match_operand:QI 1 "ext_low_reg_operand" "")
+ (match_operand:QI 2 "par_ind_operand" ""))
+ (const_int 0)))
+ (set (match_operand:QI 0 "ext_low_reg_operand" "")
+ (ior:QI (match_dup 1) (match_dup 2)))])
+ (set (match_operand:QI 3 "par_ind_operand" "")
+ (match_operand:QI 4 "ext_low_reg_operand" ""))]
+ "(REGNO (operands[0]) != REGNO (operands[4]))"
+ "or3\\t%2,%1,%0\\n||\\tsti\\t%4,%3")
+
+(define_peephole
+ [(parallel [(set (reg:CC 21)
+ (compare:CC (ior:QI (match_operand:QI 1 "par_ind_operand" "")
+ (match_operand:QI 2 "ext_low_reg_operand" ""))
+ (const_int 0)))
+ (set (match_operand:QI 0 "ext_low_reg_operand" "")
+ (ior:QI (match_dup 1) (match_dup 2)))])
+ (set (match_operand:QI 3 "par_ind_operand" "")
+ (match_operand:QI 4 "ext_low_reg_operand" ""))]
+ "(REGNO (operands[0]) != REGNO (operands[4]))"
+ "or3\\t%2,%1,%0\\n||\\tsti\\t%4,%3")
+
+(define_peephole
+ [(parallel [(set (match_operand:QI 0 "ext_low_reg_operand" "")
+ (ior:QI (match_operand:QI 1 "ext_low_reg_operand" "")
+ (match_operand:QI 2 "par_ind_operand" "")))
+ (clobber (reg:CC 21))])
+ (set (match_operand:QI 3 "par_ind_operand" "")
+ (match_operand:QI 4 "ext_low_reg_operand" ""))]
+ "(REGNO (operands[0]) != REGNO (operands[4]))"
+ "or3\\t%2,%1,%0\\n||\\tsti\\t%4,%3")
+
+(define_peephole
+ [(parallel [(set (match_operand:QI 0 "ext_low_reg_operand" "")
+ (ior:QI (match_operand:QI 1 "par_ind_operand" "")
+ (match_operand:QI 2 "ext_low_reg_operand" "")))
+ (clobber (reg:CC 21))])
+ (set (match_operand:QI 3 "par_ind_operand" "")
+ (match_operand:QI 4 "ext_low_reg_operand" ""))]
+ "(REGNO (operands[0]) != REGNO (operands[4]))"
+ "or3\\t%2,%1,%0\\n||\\tsti\\t%4,%3")
+
+(define_peephole
+ [(parallel [(set (reg:CC_NOOV 21)
+ (compare:CC_NOOV (minus:QI (match_operand:QI 1 "ext_low_reg_operand" "")
+ (match_operand:QI 2 "par_ind_operand" ""))
+ (const_int 0)))
+ (set (match_operand:QI 0 "ext_low_reg_operand" "")
+ (minus:QI (match_dup 1) (match_dup 2)))])
+ (set (match_operand:QI 3 "par_ind_operand" "")
+ (match_operand:QI 4 "ext_low_reg_operand" ""))]
+ "(REGNO (operands[0]) != REGNO (operands[4]))"
+ "subi3\\t%2,%1,%0\\n||\\tsti\\t%4,%3")
+
+(define_peephole
+ [(parallel [(set (match_operand:QI 0 "ext_low_reg_operand" "")
+ (minus:QI (match_operand:QI 1 "ext_low_reg_operand" "")
+ (match_operand:QI 2 "par_ind_operand" "")))
+ (clobber (reg:CC_NOOV 21))])
+ (set (match_operand:QI 3 "par_ind_operand" "")
+ (match_operand:QI 4 "ext_low_reg_operand" ""))]
+ "(REGNO (operands[0]) != REGNO (operands[4]))"
+ "subi3\\t%2,%1,%0\\n||\\tsti\\t%4,%3")
+
+(define_peephole
+ [(parallel [(set (reg:CC_NOOV 21)
+ (compare:CC_NOOV (minus:QF (match_operand:QF 1 "ext_low_reg_operand" "")
+ (match_operand:QF 2 "par_ind_operand" ""))
+ (match_operand:QF 3 "fp_zero_operand" "")))
+ (set (match_operand:QF 0 "ext_low_reg_operand" "")
+ (minus:QF (match_dup 1) (match_dup 2)))])
+ (set (match_operand:QF 4 "par_ind_operand" "")
+ (match_operand:QF 5 "ext_low_reg_operand" ""))]
+ "(REGNO (operands[0]) != REGNO (operands[5]))"
+ "subf3\\t%2,%1,%0\\n||\\tstf\\t%5,%4")
+
+(define_peephole
+ [(parallel [(set (match_operand:QF 0 "ext_low_reg_operand" "")
+ (minus:QF (match_operand:QF 1 "ext_low_reg_operand" "")
+ (match_operand:QF 2 "par_ind_operand" "")))
+ (clobber (reg:CC_NOOV 21))])
+ (set (match_operand:QF 3 "par_ind_operand" "")
+ (match_operand:QF 4 "ext_low_reg_operand" ""))]
+ "(REGNO (operands[0]) != REGNO (operands[4]))"
+ "subf3\\t%2,%1,%0\\n||\\tstf\\t%4,%3")
+
+(define_peephole
+ [(parallel [(set (reg:CC 21)
+ (compare:CC (xor:QI (match_operand:QI 1 "ext_low_reg_operand" "")
+ (match_operand:QI 2 "par_ind_operand" ""))
+ (const_int 0)))
+ (set (match_operand:QI 0 "ext_low_reg_operand" "")
+ (xor:QI (match_dup 1) (match_dup 2)))])
+ (set (match_operand:QI 3 "par_ind_operand" "")
+ (match_operand:QI 4 "ext_low_reg_operand" ""))]
+ "(REGNO (operands[0]) != REGNO (operands[4]))"
+ "xor3\\t%2,%1,%0\\n||\\tsti\\t%4,%3")
+
+(define_peephole
+ [(parallel [(set (reg:CC 21)
+ (compare:CC (xor:QI (match_operand:QI 1 "par_ind_operand" "")
+ (match_operand:QI 2 "ext_low_reg_operand" ""))
+ (const_int 0)))
+ (set (match_operand:QI 0 "ext_low_reg_operand" "")
+ (xor:QI (match_dup 1) (match_dup 2)))])
+ (set (match_operand:QI 3 "par_ind_operand" "")
+ (match_operand:QI 4 "ext_low_reg_operand" ""))]
+ "(REGNO (operands[0]) != REGNO (operands[4]))"
+ "xor3\\t%2,%1,%0\\n||\\tsti\\t%4,%3")
+
+(define_peephole
+ [(parallel [(set (match_operand:QI 0 "ext_low_reg_operand" "")
+ (xor:QI (match_operand:QI 1 "ext_low_reg_operand" "")
+ (match_operand:QI 2 "par_ind_operand" "")))
+ (clobber (reg:CC 21))])
+ (set (match_operand:QI 3 "par_ind_operand" "")
+ (match_operand:QI 4 "ext_low_reg_operand" ""))]
+ "(REGNO (operands[0]) != REGNO (operands[4]))"
+ "xor3\\t%2,%1,%0\\n||\\tsti\\t%4,%3")
+
+(define_peephole
+ [(parallel [(set (match_operand:QI 0 "ext_low_reg_operand" "")
+ (xor:QI (match_operand:QI 1 "par_ind_operand" "")
+ (match_operand:QI 2 "ext_low_reg_operand" "")))
+ (clobber (reg:CC 21))])
+ (set (match_operand:QI 3 "par_ind_operand" "")
+ (match_operand:QI 4 "ext_low_reg_operand" ""))]
+ "(REGNO (operands[0]) != REGNO (operands[4]))"
+ "xor3\\t%2,%1,%0\\n||\\tsti\\t%4,%3")
+
+; The following two peepholes remove an unecessary load
+; often found at the end of a function. These peepholes
+; could be generalised to other binary operators. They shouldn't
+; be required if we run a post reload mop-up pass.
+(define_peephole
+ [(parallel [(set (match_operand:QF 0 "ext_reg_operand" "")
+ (plus:QF (match_operand:QF 1 "ext_reg_operand" "")
+ (match_operand:QF 2 "ext_reg_operand" "")))
+ (clobber (reg:CC_NOOV 21))])
+ (set (match_operand:QF 3 "ext_reg_operand" "")
+ (match_dup 0))]
+ "dead_or_set_p (insn, operands[0])"
+ "addf3\\t%2,%1,%3")
+
+(define_peephole
+ [(parallel [(set (match_operand:QI 0 "reg_operand" "")
+ (plus:QI (match_operand:QI 1 "reg_operand" "")
+ (match_operand:QI 2 "reg_operand" "")))
+ (clobber (reg:CC_NOOV 21))])
+ (set (match_operand:QI 3 "reg_operand" "")
+ (match_dup 0))]
+ "dead_or_set_p (insn, operands[0])"
+ "addi3\\t%2,%1,%3")
diff --git a/gnu/egcs/gcc/config/c4x/libgcc.S b/gnu/egcs/gcc/config/c4x/libgcc.S
new file mode 100644
index 00000000000..0ce115a1ef6
--- /dev/null
+++ b/gnu/egcs/gcc/config/c4x/libgcc.S
@@ -0,0 +1,1501 @@
+/* libgcc1 routines for the Texas Instruments TMS320C[34]x
+ Copyright (C) 1997,98, 1999 Free Software Foundation, Inc.
+
+ Contributed by Michael Hayes (m.hayes@elec.canterbury.ac.nz)
+ and Herman Ten Brugge (Haj.Ten.Brugge@net.HCC.nl).
+
+
+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.
+
+In addition to the permissions in the GNU General Public License, the
+Free Software Foundation gives you unlimited permission to link the
+compiled version of this file with other programs, and to distribute
+those programs without any restriction coming from the use of this
+file. (The General Public License restrictions do apply in other
+respects; for example, they cover modification of the file, and
+distribution when not linked into another program.)
+
+This file 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 this program; 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. */
+
+
+; These routines are called using the standard TI register argument
+; passing model.
+; The following registers do not have to be saved:
+; r0, r1, r2, r3, ar0, ar1, ar2, ir0, ir1, bk, rs, rc, re, (r9, r10, r11)
+;
+; Perform floating point divqf3
+;
+; This routine performs a reciprocal of the divisor using the method
+; described in the C30/C40 user manuals. It then multiplies that
+; result by the dividend.
+;
+; Let r be the reciprocal of the divisor v and let the ith estimate
+; of r be denoted by r[i]. An iterative approach can be used to
+; improve the estimate of r, given an initial estimate r[0], where
+;
+; r[i + 1] = r[i] * (2.0 - v * r[i])
+;
+; The normalised error e[i] at the ith iteration is
+;
+; e[i] = (r - r[i]) / r = (1 / v - r[i]) * v = (1 - v * r[i])
+;
+; Note that
+;
+; e[i + 1] = (1 - v * r[i + 1]) = 1 - 2 * v * r[i] + v^2 + (r[i])^2
+; = (1 - v * r[i])^2 = (e[i])^2
+
+; r2 dividend, r3 divisor, r0 quotient
+; clobbers r1, ar1
+#ifdef L_divqf3
+ .text
+ .global ___divqf3
+___divqf3:
+
+#ifdef _TMS320C4x
+ .if .REGPARM == 0
+ lda sp,ar0
+ ldf *-ar0(2), r3
+ .endif
+
+ pop ar1 ; Pop return address
+
+; r0 = estimate of r, r1 = tmp, r2 = dividend, r3 = divisor
+ rcpf r3, r0 ; Compute initial estimate r[0]
+
+ mpyf3 r0, r3, r1 ; r1 = r[0] * v
+ subrf 2.0, r1 ; r1 = 2.0 - r[0] * v
+ mpyf r1, r0 ; r0 = r[0] * (2.0 - r[0] * v) = r[1]
+; End of 1st iteration (16 bits accuracy)
+
+ mpyf3 r0, r3, r1 ; r1 = r[1] * v
+ subrf 2.0, r1 ; r1 = 2.0 - r[1] * v
+
+ bud ar1 ; Delayed branch
+ mpyf r1, r0 ; r0 = r[1] * (2.0 - r[1] * v) = r[2]
+; End of 2nd iteration (32 bits accuracy)
+ .if .REGPARM == 0
+ mpyf *-ar0(1), r0 ; Multiply by the dividend
+ .else
+ mpyf r2, r0 ; Multiply by the dividend
+ .endif
+ rnd r0
+ ; Branch occurs here
+#else
+ .if .REGPARM == 0
+ ldiu sp,ar0
+ ldf *-ar0(2), r3
+ .endif
+
+ pop ar1 ; Pop return address
+
+; Initial estimate r[0] = 1.0 * 2^(-e - 1)
+; where v = m * 2^e
+
+; r0 = estimate of r, r1 = tmp, r2 = dividend, r3 = divisor
+
+; Calculate initial estimate r[0]
+ pushf r3
+ pop r0
+ not r0 ; r0 = -e
+ ; complement exponent = -e -1
+ ; complement sign (side effect)
+ ; complement mantissa (almost 3 bit accurate)
+ push r0
+ popf r0 ; r0 = 1.0 * e^(-e - 1) + inverted mantissa
+ ldf -1.0, r1 ; undo complement sign bit
+ xor r1, r0
+
+ mpyf3 r0, r3, r1 ; r1 = r[0] * v
+ subrf 2.0, r1 ; r1 = 2.0 - r[0] * v
+ mpyf r1, r0 ; r0 = r[0] * (2.0 - r[0] * v) = r[1]
+; End of 1st iteration
+
+ mpyf3 r0, r3, r1 ; r1 = r[1] * v
+ subrf 2.0, r1 ; r1 = 2.0 - r[1] * v
+ mpyf r1, r0 ; r0 = r[1] * (2.0 - r[1] * v) = r[2]
+; End of 2nd iteration
+
+ mpyf3 r0, r3, r1 ; r1 = r[2] * v
+ subrf 2.0, r1 ; r1 = 2.0 - r[2] * v
+ mpyf r1, r0 ; r0 = r[2] * (2.0 - r[2] * v) = r[3]
+; End of 3rd iteration
+
+ or 080h, r0 ; add 1 lsb to result. needed when complemeting
+ ; 1.0 / 2.0
+ rnd r0
+
+; Use modified last iteration
+; r[4] = (r[3] * (1.0 - (v * r[3]))) + r[3]
+ mpyf3 r0, r3, r1 ; r1 = r[3] * v
+ subrf 1.0, r1 ; r1 = 1.0 - r[3] * v
+ mpyf r0, r1 ; r1 = r[3] * (1.0 - r[3] * v)
+
+ bud ar1 ; Delayed branch
+ addf r1, r0 ; r0 = r[3] * (1.0 - r[3] * v) + r[3] = r[4]
+ .if .REGPARM == 0
+ mpyf *-ar0(1), r0 ; Multiply by the dividend
+ .else
+ mpyf r2, r0 ; Multiply by the dividend
+ .endif
+ rnd r0
+ ; Branch occurs here
+#endif
+
+#endif
+;
+; Integer signed division
+;
+; ar2 dividend, r2 divisor, r0 quotient
+; clobbers r1, r3, ar0, ar1, ir0, ir1, rc, rs, re
+#ifdef L_divqi3
+ .text
+ .global ___divqi3
+ .ref udivqi3n
+___divqi3:
+ .if .REGPARM == 0
+#ifdef _TMS320C4x
+ lda sp,ar0
+#else
+ ldiu sp,ar0
+#endif
+ ldi *-ar0(1), ar2
+ ldi *-ar0(2), r2
+ .endif
+
+ xor3 ar2, r2, r3 ; Get the sign
+ absi ar2, r0
+ bvd divq32
+ ldi r0, ar2
+ absi r2, r2
+ cmpi ar2, r2 ; Divisor > dividend?
+
+ pop ir1
+ bhid zero ; If so, return 0
+
+;
+; Normalize oeprands. Use difference exponents as shift count
+; for divisor, and as repeat count for "subc"
+;
+ float ar2, r1 ; Normalize dividend
+ pushf r1 ; Get as integer
+ pop ar0
+ lsh -24, ar0 ; Get exponent
+
+ float r2, r1 ; Normalize divisor
+ pushf r1 ; Get as integer
+ pop ir0
+ lsh -24, ir0 ; Get exponent
+
+ subi ir0, ar0 ; Get difference of exponents
+ lsh ar0, r2 ; Align divisor with dividend
+
+;
+; Do count + 1 subtracts and shifts
+;
+ rpts ar0
+ subc r2, ar2
+
+;
+; Mask off the lower count+1 bits of ar2
+;
+ subri 31, ar0 ; Shift count is (32 - (ar0 + 1))
+ lsh ar0, ar2 ; Shift left
+ negi ar0, ar0
+ lsh3 ar0, ar2, r0 ; Shift right and put result in r0
+
+;
+; Check sign and negate result if necessary
+;
+ bud ir1 ; Delayed return
+ negi r0, r1 ; Negate result
+ ash -31, r3 ; Check sign
+ ldinz r1, r0 ; If set, use negative result
+ ; Branch occurs here
+
+zero: bud ir1 ; Delayed branch
+ ldi 0, r0
+ nop
+ nop
+ ; Branch occurs here
+;
+; special case where ar2 = abs(ar2) = 0x80000000. We handle this by
+; calling unsigned divide and negating the result if necessary.
+;
+divq32:
+ push r3 ; Save sign
+ call udivqi3n
+ pop r3
+ pop ir1
+ bd ir1
+ negi r0, r1 ; Negate result
+ ash -31, r3 ; Check sign
+ ldinz r1, r0 ; If set, use negative result
+ ; Branch occurs here
+#endif
+;
+;
+; ar2 dividend, r2 divisor, r0 quotient,
+; clobbers r1, r3, ar0, ar1, ir0, ir1, rc, rs, re
+#ifdef L_udivqi3
+ .text
+ .global ___udivqi3
+ .global udivqi3n
+___udivqi3:
+ .if .REGPARM == 0
+#ifdef _TMS320C4x
+ lda sp,ar0
+#else
+ ldiu sp,ar0
+#endif
+ ldi *-ar0(1), ar2
+ ldi *-ar0(2), r2
+ .endif
+
+udivqi3n:
+ pop ir1
+
+ cmpi ar2, r2 ; If divisor > dividend
+ bhi qzero ; return zero
+ ldi r2, ar1 ; Store divisor in ar1
+
+ tstb ar2, ar2 ; Check top bit, jump if set to special handler
+ bld div_32 ; Delayed branch
+
+;
+; Get divisor exponent
+;
+ float ar1, r1 ; Normalize the divisor
+ pushf r1 ; Get into int register
+ pop rc
+ ; branch occurs here
+
+ bzd qzero ; if (float) divisor zero, return zero
+
+ float ar2, r1 ; Normalize the dividend
+ pushf r1 ; Get into int register
+ pop ar0
+ lsh -24, ar0 ; Get both the exponents
+ lsh -24, rc
+
+ subi rc, ar0 ; Get the difference between the exponents
+ lsh ar0, ar1 ; Normalize the divisor with the dividend
+
+;
+; Do count_1 subtracts and shifts
+;
+ rpts ar0
+ subc ar1, ar2
+
+;
+; mask off the lower count+1 bits
+;
+ subri 31, ar0 ; Shift count (31 - (ar0+1))
+ bud ir1 ; Delayed return
+ lsh3 ar0, ar2, r0
+ negi ar0, ar0
+ lsh ar0, r0
+ ; Branch occurs here
+
+;
+; Handle a full 32-bit dividend
+;
+div_32: tstb ar1, ar1
+ bld qone ; if divisor high bit is one, the result is one
+ lsh -24, rc
+ subri 31, rc
+ lsh rc, ar1 ; Line up the divisor
+
+;
+; Now divisor and dividend are aligned. Do first SUBC by hand, save
+; of the forst quotient digit. Then, shift divisor right rather
+; than shifting dividend left. This leaves a zero in the top bit of
+; the divident
+;
+ ldi 1, ar0 ; Initizialize MSB of quotient
+ lsh rc, ar0 ; create a mask for MSBs
+ subi 1, ar0 ; mask is (2 << count) - 1
+
+ subi3 ar1, ar2, r1
+ ldihs r1, ar2
+ ldihs 1, r1
+ ldilo 0, r1
+ lsh rc, r1
+
+ lsh -1, ar1
+ subi 1, rc
+;
+; do the rest of the shifts and subtracts
+;
+ rpts rc
+ subc ar1, ar2
+
+ bud ir1
+ and ar0, ar2
+ or3 r1, ar2, r0
+ nop
+
+qone:
+ bud ir1
+ ldi 1, r0
+ nop
+ nop
+
+qzero:
+ bud ir1
+ ldi 0, r0
+ nop
+ nop
+#endif
+
+#ifdef L_umodqi3
+ .text
+ .global ___umodqi3
+ .global umodqi3n
+___umodqi3:
+ .if .REGPARM == 0
+#ifdef _TMS320C4x
+ lda sp,ar0
+#else
+ ldiu sp,ar0
+#endif
+ ldi *-ar0(1), ar2
+ ldi *-ar0(2), r2
+ .endif
+
+umodqi3n:
+ pop ir1 ; return address
+ cmpi ar2, r2 ; divisor > dividend ?
+ bhi uzero ; if so, return dividend
+ ldi r2, ar1 ; load divisor
+;
+; If top bit of dividend is set, handle specially.
+;
+ tstb ar2, ar2 ; check top bit
+ bld umod_32 ; get divisor exponent, then jump.
+;
+; Get divisor exponent by converting to float.
+;
+ float ar1, r1 ; normalize divisor
+ pushf r1 ; push as float
+ pop rc ; pop as int to get exponent
+ bzd uzero ; if (float)divisor was zero, return
+;
+; 31 or less bits in dividend. Get dividend exponent.
+;
+ float ar2, r1 ; normalize dividend
+ pushf r1 ; push as float
+ pop ar0 ; pop as int to get exponent
+;
+; Use difference in exponents as shift count to line up MSBs.
+;
+ lsh -24, rc ; divisor exponent
+ lsh -24, ar0 ; dividend exponent
+ subi rc, ar0 ; difference
+ lsh ar0, ar1 ; shift divisor up
+;
+; Do COUNT+1 subtract & shifts.
+;
+ rpts ar0
+ subc ar1, ar2
+;
+; Remainder is in upper 31-COUNT bits.
+;
+ bud ir1 ; delayed branch to return
+ addi 1, ar0 ; shift count is COUNT+1
+ negi ar0, ar0 ; negate for right shift
+ lsh3 ar0, ar2, r0 ; shift to get result
+ ; Return occurs here
+
+;
+; The following code handles cases of a full 32-bit dividend. Before
+; SUBC can be used, the top bit must be cleared (otherwise SUBC can
+; possibly shift a significant 1 out the top of the dividend). This
+; is accomplished by first doing a normal subtraction, then proceeding
+; with SUBCs.
+;
+umod_32:
+;
+; If the top bit of the divisor is set too, the remainder is simply
+; the difference between the dividend and divisor. Otherwise, shift
+; the divisor up to line up the MSBs.
+;
+ tstb ar1, ar1 ; check divisor
+ bld uone ; if negative, remainder is diff
+
+ lsh -24, rc ; divisor exponent
+ subri 31, rc ; shift count = 31 - exp
+ negi rc, ar0 ; used later as shift count
+ lsh rc, ar1 ; shift up to line up MSBs
+;
+; Now MSBs are aligned. Do first SUBC by hand using a plain subtraction.
+; Then, shift divisor right rather than shifting dividend left. This leaves
+; a 0 in the top bit of the dividend.
+;
+ subi3 ar1, ar2, r1 ; subtract
+ ldihs r1, ar2 ; if positive, replace dividend
+ subi 1, rc ; first iteration is done
+ lsh -1, ar1 ; shift divisor down
+;
+; Do EXP subtract & shifts.
+;
+ rpts rc
+ subc ar1, ar2
+;
+; Quotient is in EXP+1 LSBs; shift remainder (in MSBs) down.
+;
+ bud ir1
+ lsh3 ar0, ar2, r0 ; COUNT contains -(EXP+1)
+ nop
+ nop
+;
+; Return (dividend - divisor).
+;
+uone: bud ir1
+ subi3 r2, ar2, r0
+ nop
+ nop
+;
+; Return dividend.
+;
+uzero: bud ir1
+ ldi ar2, r0 ; set status from result
+ nop
+ nop
+#endif
+
+#ifdef L_modqi3
+ .text
+ .global ___modqi3
+ .ref umodqi3n
+___modqi3:
+ .if .REGPARM == 0
+#ifdef _TMS320C4x
+ lda sp,ar0
+#else
+ ldiu sp,ar0
+#endif
+ ldi *-ar0(1), ar2
+ ldi *-ar0(2), r2
+ .endif
+
+;
+; Determine sign of result. Get absolute value of operands.
+;
+ ldi ar2, ar0 ; sign of result same as dividend
+ absi ar2, r0 ; make dividend positive
+ bvd mod_32 ; if still negative, escape
+ absi r2, r1 ; make divisor positive
+ ldi r1, ar1 ; save in ar1
+ cmpi r0, ar1 ; divisor > dividend ?
+
+ pop ir1 ; return address
+ bhid return ; if so, return dividend
+;
+; Normalize operands. Use difference in exponents as shift count
+; for divisor, and as repeat count for SUBC.
+;
+ float r1, r1 ; normalize divisor
+ pushf r1 ; push as float
+ pop rc ; pop as int
+ bzd return ; if (float)divisor was zero, return
+
+ float r0, r1 ; normalize dividend
+ pushf r1 ; push as float
+ pop r1 ; pop as int
+
+ lsh -24, rc ; get divisor exponent
+ lsh -24, r1 ; get dividend exponent
+ subi rc, r1 ; get difference in exponents
+ lsh r1, ar1 ; align divisor with dividend
+;
+; Do COUNT+1 subtract & shifts.
+;
+ rpts r1
+ subc ar1, r0
+;
+; Remainder is in upper bits of R0
+;
+ addi 1, r1 ; shift count is -(r1+1)
+ negi r1, r1
+ lsh r1, r0 ; shift right
+;
+; Check sign and negate result if necessary.
+;
+return:
+ bud ir1 ; delayed branch to return
+ negi r0, r1 ; negate result
+ cmpi 0, ar0 ; check sign
+ ldin r1, r0 ; if set, use negative result
+ ; Return occurs here
+;
+; The following code handles cases of a full 32-bit dividend. This occurs
+; when R0 = abs(R0) = 080000000h. Handle this by calling the unsigned mod
+; function, then negating the result if necessary.
+;
+mod_32:
+ push ar0 ; remember sign
+ call umodqi3n ; do divide
+
+ brd return ; return
+ pop ar0 ; restore sign
+ pop ir1 ; return address
+ nop
+#endif
+
+#ifdef L_unsfltconst
+ .section .const
+ .global ___unsfltconst
+___unsfltconst: .float 4294967296.0
+#endif
+
+#ifdef L_unsfltcompare
+ .section .const
+ .global ___unsfltcompare
+___unsfltcompare: .float 2147483648.0
+#endif
+
+; Integer 32-bit signed multiplication
+;
+; The TMS320C3x MPYI instruction takes two 24-bit signed integers
+; and produces a 48-bit signed result which is truncated to 32-bits.
+;
+; A 32-bit by 32-bit multiplication thus requires a number of steps.
+;
+; Consider the product of two 32-bit signed integers,
+;
+; z = x * y
+;
+; where x = (b << 16) + a, y = (d << 16) + c
+;
+; This can be expressed as
+;
+; z = ((b << 16) + a) * ((d << 16) + c)
+;
+; = ((b * d) << 32) + ((b * c + a * d) << 16) + a * c
+;
+; Let z = (f << 16) + e where f < (1 << 16).
+;
+; Since we are only interested in a 32-bit result, we can ignore the
+; (b * d) << 32 term, and thus
+;
+; f = b * c + a * d, e = a * c
+;
+; We can simplify things if we have some a priori knowledge of the
+; operands, for example, if -32768 <= y <= 32767, then y = c and d = 0 and thus
+;
+; f = b * c, e = a * c
+;
+; ar2 multiplier, r2 multiplicand, r0 product
+; clobbers r1, r2, r3
+#ifdef L_mulqi3
+ .text
+ .global ___mulqi3
+___mulqi3:
+ .if .REGPARM == 0
+#ifdef _TMS320C4x
+ lda sp,ar0
+#else
+ ldiu sp,ar0
+#endif
+ ldi *-ar0(1), ar2
+ ldi *-ar0(2), r2
+ .endif
+
+ pop ir1 ; return address
+ ldi ar2, r0 ;
+ and 0ffffh, r0 ; a
+ lsh -16, ar2 ; b
+ ldi r2, r3 ;
+ and 0ffffh, r3 ; c
+ mpyi r3, ar2 ; c * b
+ lsh -16, r2 ; d
+ mpyi r0, r2 ; a * d
+ addi ar2, r2 ; c * b + a * d
+ bd ir1 ; delayed branch to return
+ lsh 16, r2 ; (c * b + a * d) << 16
+ mpyi r3, r0 ; a * c
+ addi r2, r0 ; a * c + (c * b + a * d) << 16
+; branch occurs here
+
+#endif
+
+;
+; Integer 64 by 64 multiply
+; long1 and long2 on stack
+; result in r0,r1
+;
+#ifdef L_mulhi3
+ .text
+ .global ___mulhi3
+#ifdef _TMS320C4x
+___mulhi3:
+ pop ar0
+ ldi sp,ar2
+ ldi *-ar2(1),r2
+ ldi *-ar2(3),r3
+ mpyi3 r2,r3,r0
+ mpyuhi3 r2,r3,r1
+ mpyi *-ar2(2),r2
+ bd ar0
+ mpyi *-ar2(0),r3
+ addi r2,r1
+ addi r3,r1
+#else
+___mulhi3:
+ ldi sp,ar2
+ ldi -16,rs
+ ldi *-ar2(2),ar0
+ ldi *-ar2(4),ar1
+ ldi ar0,r2
+ and 0ffffh,r2
+ ldi ar1,r3
+ and 0ffffh,r3
+ lsh rs,ar0
+ lsh rs,ar1
+
+ mpyi r2,r3,r0
+ mpyi ar0,ar1,r1
+ mpyi r2,ar1,rc
+ lsh rs,rc,re
+ addi re,r1
+ lsh 16,rc
+ addi rc,r0
+ addc 0,r1
+ mpyi r3,ar0,rc
+ lsh rs,rc,re
+ addi re,r1
+ lsh 16,rc
+ addi rc,r0
+ addc 0,r1
+
+ ldi *-ar2(1),ar0
+ ldi ar0,r2
+ and 0ffffh,r2
+ lsh rs,ar0
+ mpyi r2,r3,rc
+ addi rc,r1
+ mpyi r2,ar1,rc
+ mpyi r3,ar0,re
+ addi re,rc
+ lsh 16,rc
+ addi rc,r1
+
+ ldi *-ar2(2),ar0
+ ldi *-ar2(3),ar1
+ ldi ar0,r2
+ and 0ffffh,r2
+ ldi ar1,r3
+ and 0ffffh,r3
+ lsh rs,ar0
+ lsh rs,ar1
+ mpyi r2,r3,rc
+ addi rc,r1
+ mpyi r2,ar1,rc
+ mpyi r3,ar0,re
+ pop ar0
+ bd ar0
+ addi re,rc
+ lsh 16,rc
+ addi rc,r1
+#endif
+#endif
+
+;
+; Integer 32 by 32 multiply highpart unsigned
+; src1 in ar2
+; src2 in r2
+; result in r0
+;
+#ifdef L_umulhi3_high
+ .text
+ .global ___umulhi3_high
+___umulhi3_high:
+ .if .REGPARM == 0
+#ifdef _TMS320C4x
+ lda sp,ar0
+#else
+ ldiu sp,ar0
+#endif
+ ldi *-ar0(1), ar2
+ ldi *-ar0(2), r2
+ .endif
+
+ ldi -16,rs
+ ldi r2,r3
+ and 0ffffh,r2
+ ldi ar2,ar1
+ and 0ffffh,ar2
+ lsh rs,r3
+ lsh rs,ar1
+
+ mpyi ar2,r2,r1
+ mpyi ar1,r3,r0
+ mpyi ar2,r3,rc
+ lsh rs,rc,re
+ addi re,r0
+ lsh 16,rc
+ addi rc,r1
+ addc 0,r0
+ mpyi r2,ar1,rc
+ lsh rs,rc,re
+ addi re,r0
+ pop ar0
+ bd ar0
+ lsh 16,rc
+ addi rc,r1
+ addc 0,r0
+#endif
+
+;
+; Integer 32 by 32 multiply highpart signed
+; src1 in ar2
+; src2 in r2
+; result in r0
+;
+#ifdef L_smulhi3_high
+ .text
+ .global ___smulhi3_high
+___smulhi3_high:
+ .if .REGPARM == 0
+#ifdef _TMS320C4x
+ lda sp,ar0
+#else
+ ldiu sp,ar0
+#endif
+ ldi *-ar0(1), ar2
+ ldi *-ar0(2), r2
+ .endif
+
+ ldi -16,rs
+ ldi 0,rc
+ subi3 ar2,rc,r0
+ ldi r2,r3
+ ldilt r0,rc
+ subi3 r2,rc,r0
+ ldi ar2,ar1
+ tstb ar1,ar1
+ ldilt r0,rc
+ and 0ffffh,r2
+ and 0ffffh,ar2
+ lsh rs,r3
+ lsh rs,ar1
+
+ mpyi ar2,r2,r1
+ mpyi ar1,r3,r0
+ addi rc,r0
+ mpyi ar2,r3,rc
+ lsh rs,rc,re
+ addi re,r0
+ lsh 16,rc
+ addi rc,r1
+ addc 0,r0
+ mpyi r2,ar1,rc
+ lsh rs,rc,re
+ addi re,r0
+ pop ar0
+ bd ar0
+ lsh 16,rc
+ addi rc,r1
+ addc 0,r0
+#endif
+
+;
+; Integer 64 by 64 unsigned divide
+; long1 and long2 on stack
+; divide in r0,r1
+; modulo in r2,r3
+; routine takes a maximum of 64*9+21=597 cycles = 24 us @ 50Mhz
+;
+#ifdef L_udivhi3
+ .text
+ .global ___udivhi3
+ .global ___udivide
+ .global ___umodulo
+ .ref udivqi3n
+ .ref umodqi3n
+___udivhi3:
+ ldi sp,ar2
+ ldi *-ar2(4),ar0
+ ldi *-ar2(3),ar1
+ ldi *-ar2(2),r0
+ ldi *-ar2(1),r1
+
+___udivide:
+ or r1,ar1,r2
+ bne udiv0
+ ldi ar0,r2
+ ldi r0,ar2
+ call udivqi3n
+ ldiu 0,r1
+ rets
+
+___umodulo:
+ or r1,ar1,r2
+ bne udiv0
+ ldi ar0,r2
+ ldi r0,ar2
+ call umodqi3n
+ ldi r0,r2
+ ldiu 0,r3
+ rets
+
+udiv0:
+ tstb ar1,ar1
+ bne udiv1
+ tstb ar0,ar0
+ bn udiv1
+
+ ldiu 63,rc
+#ifdef _TMS320C4x
+ rptbd udivend0
+ ldiu 0,r2
+ addi r0,r0
+ rolc r1
+#else
+ ldiu 0,r2
+ addi r0,r0
+ rolc r1
+ rptb udivend0
+#endif
+
+ rolc r2
+ subi3 ar0,r2,r3
+ xor 1,st
+ ldic r3,r2
+ rolc r0
+udivend0:
+ rolc r1
+
+ ldiu 0,r3
+ rets
+udiv1:
+ push r4
+ push r5
+ ldiu 63,rc
+ ldiu 0,r2
+#ifdef _TMS320C4x
+ rptbd udivend1
+ ldiu 0,r3
+ addi r0,r0
+ rolc r1
+#else
+ ldiu 0,r3
+ addi r0,r0
+ rolc r1
+ rptb udivend1
+#endif
+
+ rolc r2
+ rolc r3
+ subi3 ar0,r2,r4
+ subb3 ar1,r3,r5
+ xor 1,st
+ ldic r4,r2
+ ldic r5,r3
+ rolc r0
+udivend1:
+ rolc r1
+
+ pop r5
+ pop r4
+ rets
+#endif
+
+;
+; Integer 64 by 64 unsigned modulo
+; long1 and long2 on stack
+; result in r0,r1
+;
+#ifdef L_umodhi3
+ .text
+ .global ___umodhi3
+ .ref ___modulo
+___umodhi3:
+ ldi sp,ar2
+ ldi *-ar2(4),ar0
+ ldi *-ar2(3),ar1
+ ldi *-ar2(2),r0
+ ldi *-ar2(1),r1
+ call ___umodulo
+ pop ar0
+ bd ar0
+ ldi r2,r0
+ ldi r3,r1
+ nop
+#endif
+
+;
+; Integer 64 by 64 signed divide
+; long1 and long2 on stack
+; result in r0,r1
+;
+#ifdef L_divhi3
+ .text
+ .global ___divhi3
+ .ref ___udivide
+___divhi3:
+ ldi 0,ir0
+ ldi sp,ar2
+ ldi *-ar2(4),r0
+ ldi *-ar2(3),r1
+ bge div1
+ not ir0
+ negi r0
+ negb r1
+div1:
+ ldi r0,ar0
+ ldi r1,ar1
+ ldi *-ar2(2),r0
+ ldi *-ar2(1),r1
+ bge div2
+ not ir0
+ negi r0
+ negb r1
+div2:
+ call ___udivide
+ tstb ir0,ir0
+ bge div3
+ negi r0
+ negb r1
+div3:
+ rets
+#endif
+
+;
+; Integer 64 by 64 signed modulo
+; long1 and long2 on stack
+; result in r0,r1
+;
+#ifdef L_modhi3
+ .text
+ .global ___modhi3
+ .ref ___umodulo
+___modhi3:
+ ldi 0,ir0
+ ldi sp,ar2
+ ldi *-ar2(4),r0
+ ldi *-ar2(3),r1
+ bge mod1
+ not ir0
+ negi r0
+ negb r1
+mod1:
+ ldi r0,ar0
+ ldi r1,ar1
+ ldi *-ar2(2),r0
+ ldi *-ar2(1),r1
+ bge mod2
+ not ir0
+ negi r0
+ negb r1
+mod2:
+ call ___umodulo
+ ldi r2,r0
+ ldi r3,r1
+ tstb ir0,ir0
+ bge mod3
+ negi r0
+ negb r1
+mod3:
+ rets
+#endif
+
+;
+; double to signed long long converion
+; input in r2
+; result in r0,r1
+;
+#ifdef L_fix_truncqfhi2
+ .text
+ .global ___fix_truncqfhi2
+ .ref ufix_truncqfhi2n
+___fix_truncqfhi2:
+ .if .REGPARM == 0
+#ifdef _TMS320C4x
+ lda sp,ar0
+#else
+ ldiu sp,ar0
+#endif
+ ldf *-ar0(1), r2
+ .endif
+
+ cmpf 0.0,r2
+ bge ufix_truncqfhi2n
+ negf r2
+ call ufix_truncqfhi2n
+ negi r0
+ negb r1
+ rets
+#endif
+
+;
+; double to unsigned long long converion
+; input in r2
+; result in r0,r1
+;
+#ifdef L_ufix_truncqfhi2
+ .text
+ .global ___ufix_truncqfhi2
+ .global ufix_truncqfhi2n
+___ufix_truncqfhi2:
+ .if .REGPARM == 0
+#ifdef _TMS320C4x
+ lda sp,ar0
+#else
+ ldiu sp,ar0
+#endif
+ ldf *-ar0(1), r2
+ .endif
+
+ufix_truncqfhi2n:
+ cmpf 0.0,r2
+ ble ufix1
+ pushf r2
+ pop r3
+ ash -24,r3
+ subi 31,r3
+ cmpi 32,r3
+ bge ufix1
+ cmpi -32,r3
+ ble ufix1
+ ldi 1,r0
+ ash 31,r0
+ or3 r0,r2,r0
+ ldi r0,r1
+ lsh3 r3,r0,r0
+ subi 32,r3
+ cmpi -32,r3
+ ldile 0,r1
+ lsh3 r3,r1,r1
+ rets
+ufix1:
+ ldi 0,r0
+ ldi 0,r1
+ rets
+#endif
+
+;
+; signed long long to double converion
+; input on stack
+; result in r0
+;
+#ifdef L_floathiqf2
+ .text
+ .global ___floathiqf2
+ .ref ufloathiqf2n
+___floathiqf2:
+ ldi sp,ar2
+ ldi *-ar2(2),r0
+ ldi *-ar2(1),r1
+ bge ufloathiqf2n
+ negi r0
+ negb r1
+ call ufloathiqf2n
+ negf r0
+ rets
+#endif
+
+;
+; unsigned long long to double converion
+; input on stack
+; result in r0
+;
+#ifdef L_ufloathiqf2
+ .text
+ .global ___ufloathiqf2
+ .global ufloathiqf2n
+ .ref ___unsfltconst
+___ufloathiqf2:
+ ldi sp,ar2
+ ldi *-ar2(2),r0
+ ldi *-ar2(1),r1
+ufloathiqf2n:
+ .if .BIGMODEL
+#ifdef _TMS320C4x
+ ldpk @___unsfltconst
+#else
+ ldp @___unsfltconst
+#endif
+ .endif
+ ldf @___unsfltconst,r2
+ float r0
+ bge uflt1
+ addf r2,r0
+uflt1:
+ float r1
+ bge uflt2
+ addf r2,r1
+uflt2:
+#ifdef _TMS320C4x
+ pop r3
+ bd r3
+ mpyf r2,r1
+ addf r1,r0
+ nop
+#else
+ ldf r1,r3
+ and 0ffh,r3
+ norm r3,r3
+ mpyf r2,r3
+ pop ar2
+ bd ar2
+ addf r3,r0
+ mpyf r2,r1
+ addf r1,r0
+#endif
+#endif
+
+;
+; long double to signed long long converion
+; input in r2
+; result in r0,r1
+;
+#ifdef L_fix_trunchfhi2
+ .text
+ .global ___fix_trunchfhi2
+ .ref ufix_trunchfhi2n
+___fix_trunchfhi2:
+ .if .REGPARM == 0
+#ifdef _TMS320C4x
+ lda sp,ar0
+#else
+ ldiu sp,ar0
+#endif
+ ldf *-ar0(2), r2
+ ldi *-ar0(1), r2
+ .endif
+
+ cmpf 0.0,r2
+ bge ufix_trunchfhi2n
+ negf r2
+ call ufix_trunchfhi2n
+ negi r0
+ negb r1
+ rets
+#endif
+
+;
+; long double to unsigned long long converion
+; input in r2
+; result in r0,r1
+;
+#ifdef L_ufix_trunchfhi2
+ .text
+ .global ___ufix_trunchfhi2
+ .global ufix_trunchfhi2n
+___ufix_trunchfhi2:
+ .if .REGPARM == 0
+#ifdef _TMS320C4x
+ lda sp,ar0
+#else
+ ldiu sp,ar0
+#endif
+ ldf *-ar0(2), r2
+ ldi *-ar0(1), r2
+ .endif
+
+ufix_trunchfhi2n:
+ cmpf 0.0,r2
+ ble ufixh1
+ pushf r2
+ pop r3
+ ash -24,r3
+ subi 31,r3
+ cmpi 32,r3
+ bge ufixh1
+ cmpi -32,r3
+ ble ufixh1
+ ldi 1,r0
+ ash 31,r0
+ or3 r0,r2,r0
+ ldi r0,r1
+ lsh3 r3,r0,r0
+ subi 32,r3
+ cmpi -32,r3
+ ldile 0,r1
+ lsh3 r3,r1,r1
+ rets
+ufixh1:
+ ldi 0,r0
+ ldi 0,r1
+ rets
+#endif
+
+;
+; signed long long to long double converion
+; input on stack
+; result in r0
+;
+#ifdef L_floathihf2
+ .text
+ .global ___floathihf2
+ .ref ufloathihf2n
+___floathihf2:
+ ldi sp,ar2
+ ldi *-ar2(2),r0
+ ldi *-ar2(1),r1
+ bge ufloathihf2n
+ negi r0
+ negb r1
+ call ufloathihf2n
+ negf r0
+ rets
+#endif
+
+;
+; unsigned long long to double converion
+; input on stack
+; result in r0
+;
+#ifdef L_ufloathihf2
+ .text
+ .global ___ufloathihf2
+ .global ufloathihf2n
+ .ref ___unsfltconst
+___ufloathihf2:
+ ldi sp,ar2
+ ldi *-ar2(2),r0
+ ldi *-ar2(1),r1
+ufloathihf2n
+ .if .BIGMODEL
+#ifdef _TMS320C4x
+ ldpk @___unsfltconst
+#else
+ ldp @___unsfltconst
+#endif
+ .endif
+ ldf @___unsfltconst,r2
+ float r0
+ bge uflth1
+ addf r2,r0
+uflth1:
+ float r1
+ bge uflth2
+ addf r2,r1
+uflth2:
+#ifdef _TMS320C4x
+ pop r3
+ bd r3
+ mpyf r2,r1
+ addf r1,r0
+ nop
+#else
+ ldf r1,r3
+ and 0ffh,r3
+ norm r3,r3
+ mpyf r2,r3
+ pop ar2
+ bd ar2
+ addf r3,r0
+ mpyf r2,r1
+ addf r1,r0
+#endif
+#endif
+
+;
+; calculate ffs
+; input in ar2
+; result in r0
+;
+#ifdef L_ffs
+ .global ___ffs
+ .ref ___unsfltconst
+ .text
+___ffs:
+ .if .REGPARM == 0
+#ifdef _TMS320C4x
+ lda sp,ar0
+#else
+ ldiu sp,ar0
+#endif
+ ldi *-ar0(1), ar2
+ .endif
+
+ negi ar2,r0
+ and ar2,r0
+ float r0,r0
+ ldfu 0.0,r1
+ .if .BIGMODEL
+#ifdef _TMS320C4x
+ ldpk @___unsfltconst
+#else
+ ldp @___unsfltconst
+#endif
+ .endif
+ ldflt @___unsfltconst,r1
+ addf r1,r0
+ pushf r0
+ pop r0
+ pop ar0
+ bd ar0
+ ash -24,r0
+ ldilt -1,r0
+ addi 1,r0
+#endif
+
+;
+; calculate long double * long double
+; input in r2, r3
+; output in r0
+;
+#ifdef L_mulhf3
+ .global ___mulhf3
+ .text
+___mulhf3:
+ .if .REGPARM == 0
+#ifdef _TMS320C4x
+ lda sp,ar0
+#else
+ ldiu sp,ar0
+#endif
+ ldf *-ar0(2), r2
+ ldi *-ar0(1), r2
+ ldf *-ar0(4), r3
+ ldi *-ar0(3), r3
+ .endif
+
+ pop ar2 ; return ad
+ ldf r2,r0 ; copy lsb0
+ ldf r3,r1 ; copy lsb1
+ and 0ffh,r0 ; mask lsb0
+ and 0ffh,r1 ; mask lsb1
+ norm r0,r0 ; correct lsb0
+ norm r1,r1 ; correct lsb1
+ mpyf r2,r1 ; arg0*lsb1
+ mpyf r3,r0 ; arg1*lsb0
+ bd ar2 ; return (delayed)
+ addf r0,r1 ; arg0*lsb1 + arg1*lsb0
+ mpyf r2,r3,r0 ; msb0*msb1
+ addf r1,r0 ; msb0*msb1 + arg0*lsb1 + arg1*lsb0
+#endif
+
+;
+; calculate long double / long double
+; r2 dividend, r3 divisor, r0 quotient
+;
+#ifdef L_divhf3
+ .global ___divhf3
+ .text
+___divhf3:
+ .if .REGPARM == 0
+#ifdef _TMS320C4x
+ lda sp,ar0
+#else
+ ldiu sp,ar0
+#endif
+ ldf *-ar0(2), r2
+ ldi *-ar0(1), r2
+ ldf *-ar0(4), r3
+ ldi *-ar0(3), r3
+ .endif
+
+#ifdef _TMS320C4x
+ pop ar1
+ rcpf r3, r0
+ mpyf3 r0, r3, r1
+ subrf 2.0, r1
+ mpyf r1, r0
+ mpyf3 r0, r3, r1
+ bud ar1
+ subrf 2.0, r1
+ mpyf r1, r0
+ mpyf r2, r0
+#else
+ pop ar1
+ pushf r3
+ pop r0
+ not r0
+ push r0
+ popf r0
+ ldf -1.0, r1
+ xor r1, r0
+
+ mpyf3 r0, r3, r1 ; r1 = r[0] * v
+ subrf 2.0, r1 ; r1 = 2.0 - r[0] * v
+ mpyf r1, r0 ; r0 = r[0] * (2.0 - r[0] * v) = r[1]
+; End of 1st iteration
+
+ mpyf3 r0, r3, r1 ; r1 = r[1] * v
+ subrf 2.0, r1 ; r1 = 2.0 - r[1] * v
+ mpyf r1, r0 ; r0 = r[1] * (2.0 - r[1] * v) = r[2]
+; End of 2nd iteration
+
+ mpyf3 r0, r3, r1 ; r1 = r[2] * v
+ subrf 2.0, r1 ; r1 = 2.0 - r[2] * v
+ mpyf r1, r0 ; r0 = r[2] * (2.0 - r[2] * v) = r[3]
+; End of 3rd iteration
+
+ or 080h, r0
+ rnd r0
+
+; mpyf3 r0, r3, r1 ; r1 = r[3] * v
+ push r4
+ pushf r4
+ mpyf r0, r3, r1
+
+ ldf r0, r4
+ and 0ffh, r4
+ norm r4, r4
+ mpyf r3, r4
+ addf r4, r1
+
+ ldf r3, r4
+ and 0ffh, r4
+ norm r4, r4
+ mpyf r0, r4
+ addf r4, r1
+
+ subrf 2.0, r1 ; r1 = 2.0 - r[3] * v
+
+ mpyf r1, r0, r3 ; r3 = r[3] * (2.0 - r[3] * v) = r[5]
+
+ ldf r1, r4
+ and 0ffh, r4
+ norm r4, r4
+ mpyf r0, r4
+ addf r4, r3
+
+ ldf r0, r4
+ and 0ffh, r4
+ norm r4, r4
+ mpyf r1, r4
+ addf r4, r3
+
+ mpyf r2, r3, r0 ; Multiply by the dividend
+
+ ldf r2, r4
+ and 0ffh, r4
+ norm r4, r4
+ mpyf r3, r4
+ addf r4, r0
+
+ ldf r3, r4
+ and 0ffh, r4
+ norm r4, r4
+ mpyf r2, r4
+ bd ar1
+ addf r4, r0
+
+ popf r4
+ pop r4
+#endif
+#endif
diff --git a/gnu/egcs/gcc/config/c4x/t-c4x b/gnu/egcs/gcc/config/c4x/t-c4x
new file mode 100644
index 00000000000..3b3dbd6ea06
--- /dev/null
+++ b/gnu/egcs/gcc/config/c4x/t-c4x
@@ -0,0 +1,20 @@
+CROSS_LIBGCC1 = libgcc1-asm.a
+LIB1ASMSRC = c4x/libgcc.S
+LIB1ASMFUNCS = _divqf3 _divqi3 _udivqi3 _umodqi3 _modqi3 _mulqi3 \
+ _mulhf3 _divhf3 _unsfltconst _unsfltcompare \
+ _mulhi3 _umulhi3_high _smulhi3_high _divhi3 _modhi3 _umodhi3 _udivhi3 \
+ _fix_truncqfhi2 _ufix_truncqfhi2 _floathiqf2 _ufloathiqf2 \
+ _floathihf2 _ufloathihf2 _fix_trunchfhi2 _ufix_trunchfhi2 _ffs
+
+TARGET_LIBGCC2_CFLAGS = -Dexit=unused_exit -DDF=HF -DDI=HI -DSF=QF -DSI=QI -Dinhibit_libc
+
+MULTILIB_OPTIONS = m30 msmall mmemparm
+MULTILIB_DIRNAMES = c3x small mem
+MULTILIB_MATCHES = m30=mcpu?30 m30=mcpu?31 m30=mcpu?32 m30=m31 m30=m32
+MULTILIB_EXCEPTIONS =
+MULTILIB_EXTRA_OPTS =
+LIBGCC = stmp-multilib
+INSTALL_LIBGCC = install-multilib
+
+# Don't make libgcc1-test since require crt0.o
+LIBGCC1_TEST =
diff --git a/gnu/egcs/gcc/config/c4x/xm-c4x.h b/gnu/egcs/gcc/config/c4x/xm-c4x.h
new file mode 100644
index 00000000000..dc329ebc9ba
--- /dev/null
+++ b/gnu/egcs/gcc/config/c4x/xm-c4x.h
@@ -0,0 +1,21 @@
+/* #defines that need visibility everywhere. */
+#define FALSE 0
+#define TRUE 1
+
+/* This describes the machine the compiler is hosted on. */
+#define HOST_BITS_PER_CHAR 32
+#define HOST_BITS_PER_SHORT 32
+#define HOST_BITS_PER_INT 32
+#define HOST_BITS_PER_LONG 32
+#define HOST_BITS_PER_LONGLONG 64
+
+#define HOST_WORDS_BIG_ENDIAN
+
+/* target machine dependencies.
+ tm.h is a symbolic link to the actual target specific file. */
+#include "tm.h"
+
+/* Arguments to use with `exit'. */
+#define SUCCESS_EXIT_CODE 0
+#define FATAL_EXIT_CODE 33
+