From cb0ca2844b8681305bcad7699657a7967cc9c634 Mon Sep 17 00:00:00 2001 From: Michael Hayes Date: Sat, 19 Sep 1998 00:03:07 +0000 Subject: [PATCH] README.C4X: New file with information about the c4x ports. * README.C4X: New file with information about the c4x ports. * ginclude/va-c4x.h: New file for c4x varargs support. * config/c4x: New directory with c4x port files. From-SVN: r22475 --- gcc/ChangeLog | 6 + gcc/README.C4X | 48 + gcc/config/c4x/c4x.c | 5597 ++++++++++++++++++++++++++++++++++++++ gcc/config/c4x/c4x.h | 2608 ++++++++++++++++++ gcc/config/c4x/c4x.md | 6798 +++++++++++++++++++++++++++++++++++++++++++++++ gcc/config/c4x/t-c4x | 29 + gcc/config/c4x/xm-c4x.h | 21 + gcc/ginclude/va-c4x.h | 34 + 8 files changed, 15141 insertions(+) create mode 100644 gcc/README.C4X create mode 100644 gcc/config/c4x/c4x.c create mode 100644 gcc/config/c4x/c4x.h create mode 100644 gcc/config/c4x/c4x.md create mode 100644 gcc/config/c4x/t-c4x create mode 100644 gcc/config/c4x/xm-c4x.h create mode 100644 gcc/ginclude/va-c4x.h diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 4519495..6caede7 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,9 @@ +Sat Sep 19 01:00:32 1998 Michael Hayes (mph@elec.canterbury.ac.nz) + + * README.C4X: New file with information about the c4x ports. + * ginclude/va-c4x.h: New file for c4x varargs support. + * config/c4x: New directory with c4x port files. + Fri Sep 18 22:52:05 1998 Jeffrey A Law (law@cygnus.com) * reload.c (find_reloads): Do not replace a pseudo with diff --git a/gcc/README.C4X b/gcc/README.C4X new file mode 100644 index 0000000..c8c5f22 --- /dev/null +++ b/gcc/README.C4X @@ -0,0 +1,48 @@ +This file describes the implementation notes of the GNU C Compiler for +the Texas Instruments Floating Point Digital Signal Processor +families, TMS320C3x and TMS320C4x (including the C30, C31, C32, C40, +and C44 chips). + + +Currently, only two code variants are generated---those for the C3x +and C4x architectures. Note that the new operand combinations for +parallel instructions, included in newer silicon revisions, are not +yet supported. These should be trivial to add for someone with the +newer chips and the inclination. + + +While the generated assembly code is fairly similar to that recognised +by the TI assembler, there are a few differences (currently the machine +option -mti, designed to enfore compatibility, is not fully +implemented). The major difference is the use of the ^ operator to +load the 16 MSBs of an address or constant for the C4x. + + +The generated assembly code requires the GNU assembler (GAS). This is +not currently included as part of the binutils package, due to the +many hacks required to be compatible with TI's kludged COFF +implementation, and the binutils not being designed for 32-bit bytes. +Patches against binutils-2.7.2 can be obtained from +ftp://ongaonga.chch.cri.nz/pub/c4x. This site also has patches for +the GNU debugger (GDB), incoporating a cycle accurate simulator that +can display profiles and histories of code execution, detailing +pipeline conflicts etc. + + +GCC can be configured as a cross compiler for both the C3x and C4x +architectures on the same system. Use `configure --target=c4x' to +configure GCC for both the C3x and C4x. Then use the -m30 option to +generate code for the C30 or -m40 (the default) for the C40. + + +Further installation notes and other optimization patches for GCC can +also be obtained from ftp://ongaonga.chch.cri.nz/pub/c4x. + + +A Majordomo mailing list, gcc_c40@atlantek.com.au, exists to discuss +related issues and suggestions for further optimizations. To +subscribe send a message with `subscribe gcc_c40' in the body to +majordomo@atlantek.com.au. + + +Michael Hayes, 16 Sep 98 diff --git a/gcc/config/c4x/c4x.c b/gcc/config/c4x/c4x.c new file mode 100644 index 0000000..f6b75c1 --- /dev/null +++ b/gcc/config/c4x/c4x.c @@ -0,0 +1,5597 @@ +/* Subroutines for assembler code output on the TMS320C[34]x + Copyright (C) 1994, 1995, 1996, 1997 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 +#include +#include +#include "config.h" +#include "gansidecl.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" + +extern void iteration_info (); /* in unroll.c */ + +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 */ + INT_REGS, /* 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 */ + QImode, /* 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 */ +}; + + +/* rptb_info has enough information to compute rtx for loop counter. */ +typedef struct +{ + int loop_count; /* Positive if loop count is constant */ + /* The rest of fields are meaningless if loop_count is set */ + rtx start_value; /* Starting value for biv */ + rtx end_value; /* Limit for biv */ + int swap_p; /* 1 for count down */ + int incr; /* Increment for biv -- must be constant */ + int shift; /* log2(incr) */ + int off_by_one; /* 1 for "<", 0 for "<=" */ + int unsigned_p; /* True if unsigned comparison at loop end */ + rtx loop_start; +} +c4x_rptb_info_t; + +/* 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; + +static void +c4x_dump (file, s) + FILE * file; + const char *s; + ... +{ +#ifndef __STDC__ + char *s; +#endif + va_list ap; + + if (!file) + return; + + VA_START (ap, s); + +#ifndef __STDC__ + s = va_arg (ap, char *); +#endif + + vfprintf (file, s, ap); + va_end (ap); +} + +/* 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 () +{ + /* Convert foo / 8.0 into foo * 0.125, etc. */ + flag_fast_math = 1; + + 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; + +} + + +/* 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; +} + + +void +c4x_emit_libcall (name, code, dmode, smode, noperands, operands) + const 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) + const 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_rtx (CONST_INT, VOIDmode, 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; + enum reg_class class; +{ + if (GET_CODE (x) == MEM && class > ADDR_REGS && class != INDEX_REGS) + { + x = XEXP (x, 0); + if (GET_CODE (x) == PLUS) + { + rtx op0 = XEXP (x, 0); + rtx op1 = XEXP (x, 1); + + if (REG_P (op0) + && IS_ADDR_REGNO (op0) + && GET_CODE (op1) == CONST_INT + && !IS_DISP8_CONST (INTVAL (op1))) + class = ADDR_REGS; + } + } + return class; +} + + +enum reg_class +c4x_limit_reload_class (mode, class) + enum machine_mode mode; + enum reg_class class; +{ + return class; +} + + +enum reg_class +c4x_secondary_memory_needed (class1, class2, mode) + enum reg_class class1; + enum reg_class class2; + enum machine_mode mode; +{ + return 0; +} + + +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 POST_DEC: + if (mode != QImode && mode != QFmode) + return 0; + case PRE_INC: + 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 USE: + /* The uses are put in to avoid problems + with referenced things disappearing. */ + return c4x_check_legit_addr (mode, op1, strict); + + case PLUS: + /* This is another reference to keep things + from disappearing, but it contains a plus + of a use and DP. */ + if (GET_CODE (XEXP (op0, 0)) == USE) + return c4x_check_legit_addr (mode, op1, strict); + return 0; + + 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 some work for the assembler... */ + case CONST: + if (GET_CODE (XEXP (addr, 0)) == PLUS + && (GET_CODE (XEXP (XEXP (addr, 0), 0)) == SYMBOL_REF + || GET_CODE (XEXP (XEXP (addr, 0), 0)) == LABEL_REF) + && GET_CODE (XEXP (XEXP (addr, 0), 1)) == CONST_INT) + return 1; + + /* Direct addressing. */ + case SYMBOL_REF: + case LABEL_REF: + return 1; + + /* 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; + enum machine_mode mode; +{ + 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 CONST: + { + rtx offset = const0_rtx; + addr = eliminate_constant_term (addr, &offset); + + if (GET_CODE (addr) == LABEL_REF) + return 3; + + if (GET_CODE (addr) != SYMBOL_REF) + return 4; + + if (INTVAL (offset) == 0) + return 3; + } + + /* fall through */ + + case POST_INC: + case POST_DEC: + case PRE_INC: + case PRE_DEC: + return 1; + + case SYMBOL_REF: + case LABEL_REF: + return TARGET_SMALL ? 3 : 4; + + 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: + 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 (reversed, insn) + int reversed; + rtx insn; +{ + int delayed = 0; + int annultrue = 0; + int annulfalse = 0; + rtx delay; + char *cp; + static char str[20]; + + if (final_sequence) + { + delay = XVECEXP (final_sequence, 0, 1); + delayed = !INSN_ANNULLED_BRANCH_P (insn); + annultrue = INSN_ANNULLED_BRANCH_P (insn) && !INSN_FROM_TARGET_P (delay); + annulfalse = INSN_ANNULLED_BRANCH_P (insn) && INSN_FROM_TARGET_P (delay); + } + cp = str; + *cp++ = 'b'; + *cp++ = '%'; + if (reversed) + *cp++ = 'I'; + *cp++ = '0'; + 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; /* % 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 'C': /* call */ + if (code != MEM) + fatal_insn ("c4x_print_operand: %%C inconsistency", op); + op1 = XEXP (op, 0); + SYMBOL_REF_FLAG (op1) = 1; + output_addr_const (file, op1); + return; + + case 'H': /* sethi */ + if (code == SYMBOL_REF) + SYMBOL_REF_FLAG (op) = 1; + break; + + 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)) == PLUS + && 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 'R': /* call register */ + op1 = XEXP (op, 0); + if (code != MEM || GET_CODE (op1) != REG) + fatal_insn ("c4x_print_operand: %%R inconsistency", op); + else + fprintf (file, "%s", reg_names[REGNO (op1)]); + 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); + enum rtx_code code0 = GET_CODE (op0); + + if (code0 == USE || code0 == PLUS) + { + asm_fprintf (file, "@"); + output_addr_const (file, op1); + } + else if (REG_P (op0)) + { + if (REGNO (op0) == DP_REGNO) + { + c4x_print_operand_address (file, op1); + } + else 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 */ + } + } + } + break; + + case CONST: + case SYMBOL_REF: + case LABEL_REF: + if (!SYMBOL_REF_FLAG (addr)) + fprintf (file, "@"); + output_addr_const (file, addr); + SYMBOL_REF_FLAG (addr) = 0; + break; + + /* We shouldn't access CONST_INT addresses. */ + case CONST_INT: + + default: + fatal_insn ("c4x_print_operand_address: Bad operand case", addr); + break; + } +} + + +static int +c4x_immed_float_p (operand) + rtx operand; +{ + long convval[2]; + int exponent; + REAL_VALUE_TYPE r; + + REAL_VALUE_FROM_CONST_DOUBLE (r, operand); + if (GET_MODE (operand) == 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 */ +} + + +/* This function checks for an insn operand that requires direct + addressing and inserts a load of the DP register prior to the + insn if the big memory model is being compiled for. Immediate + operands that do not fit within the opcode field get changed + into memory references using direct addressing. At this point + all pseudos have been converted to hard registers. */ + +int +c4x_scan_for_ldp (newop, insn, operand0) + rtx *newop; + rtx insn; + rtx operand0; +{ + int i; + char *format_ptr; + rtx op0, op1, op2, addr; + rtx operand = *newop; + + switch (GET_CODE (operand)) + { + case MEM: + op0 = XEXP (operand, 0); + + /* We have something we need to emit a load dp insn for. + The first operand should hold the rtx for the instruction + required. */ + + switch (GET_CODE (op0)) + { + case CONST_INT: + fatal_insn ("c4x_scan_for_ldp: Direct memory access to const_int", + op0); + break; + + case CONST: + case SYMBOL_REF: + if (!TARGET_C3X && !TARGET_SMALL + && recog_memoized (insn) == CODE_FOR_movqi_noclobber + && ((addr = find_reg_note (insn, REG_EQUAL, NULL_RTX)) + || (addr = find_reg_note (insn, REG_EQUIV, NULL_RTX))) + && (IS_STD_OR_PSEUDO_REGNO (operand0))) + { + addr = XEXP (addr, 0); + if (GET_CODE (addr) == CONST_INT) + { + op1 = gen_rtx (CONST_INT, VOIDmode, INTVAL (addr) & ~0xffff); + emit_insn_before (gen_movqi (operand0, op1), insn); + op1 = gen_rtx (CONST_INT, VOIDmode, INTVAL (addr) & 0xffff); + emit_insn_before (gen_iorqi3_noclobber (operand0, + operand0, op1), insn); + delete_insn (insn); + return 1; + } + else if (GET_CODE (addr) == SYMBOL_REF) + { + emit_insn_before (gen_set_high_use (operand0, addr, addr), + insn); + emit_insn_before (gen_set_ior_lo_use (operand0, addr, addr), + insn); + delete_insn (insn); + return 1; + } + else if (GET_CODE (addr) == CONST + && GET_CODE (op1 = XEXP (addr, 0)) == PLUS + && GET_CODE (op2 = XEXP (op1, 0)) == SYMBOL_REF + && GET_CODE (XEXP (op1, 1)) == CONST_INT) + { + emit_insn_before (gen_set_high_use (operand0, addr, op2), + insn); + emit_insn_before (gen_set_ior_lo_use (operand0, addr, op2), + insn); + delete_insn (insn); + return 1; + } + } + if (!TARGET_SMALL) + emit_insn_before (gen_set_ldp (gen_rtx (REG, Pmode, DP_REGNO), + operand), insn); + + /* Replace old memory reference with direct reference. */ + *newop = gen_rtx (MEM, GET_MODE (operand), + gen_rtx (PLUS, Pmode, + gen_rtx (REG, Pmode, DP_REGNO), op0)); + + /* Use change_address? */ + MEM_VOLATILE_P (*newop) = MEM_VOLATILE_P (operand); + RTX_UNCHANGING_P (*newop) = RTX_UNCHANGING_P (operand); + MEM_IN_STRUCT_P (*newop) = MEM_IN_STRUCT_P (operand); + break; + + default: + break; + } + + return 0; + + case CONST_INT: + if (SMALL_CONST (INTVAL (operand), insn)) + break; + fatal_insn ("Immediate integer too large", insn); + + case CONST_DOUBLE: + if (c4x_immed_float_p (operand)) + break; + + /* We'll come here if a CONST_DOUBLE integer has slipped + though the net... */ + fatal_insn ("Immediate CONST_DOUBLE integer too large", insn); + + case CONST: + fatal_insn ("Immediate integer not known", insn); + + /* Symbol and label immediate addresses cannot be stored + within a C[34]x instruction, so we store them in memory + and use direct addressing instead. */ + case LABEL_REF: + case SYMBOL_REF: + if (GET_CODE (operand0) != REG) + break; + + op0 = XEXP (force_const_mem (Pmode, operand), 0); + *newop = gen_rtx (MEM, GET_MODE (operand), + gen_rtx (PLUS, Pmode, + gen_rtx (PLUS, Pmode, + gen_rtx (USE, VOIDmode, operand), + gen_rtx (REG, Pmode, DP_REGNO)), + op0)); + + if (!TARGET_SMALL) + emit_insn_before (gen_set_ldp_use (gen_rtx (REG, Pmode, DP_REGNO), + *newop, operand), insn); + return 0; + + default: + break; + } + + format_ptr = GET_RTX_FORMAT (GET_CODE (operand)); + + /* Recursively hunt for required loads of DP. */ + for (i = 0; i < GET_RTX_LENGTH (GET_CODE (operand)); i++) + { + if (*format_ptr++ == 'e') /* rtx expression */ + if (c4x_scan_for_ldp (&XEXP (operand, i), insn, operand0)) + break; + } + return 0; +} + + +/* 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.) */ + +int +c4x_rptb_nop_p (insn) + rtx insn; +{ + int i; + + /* If there is a label at the end of the loop we must insert + a NOP. */ + insn = prev_nonnote_insn (insn); + 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) + insn = PREV_INSN (insn); + + /* I 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; + else if (recog_memoized (insn) == CODE_FOR_rptb_top) + return i == 0; + insn = PREV_INSN (insn); + } + return 0; +} + + +/* This function is a C4x special. It scans through all the insn + operands looking for places where the DP register needs to be + reloaded and for large immediate operands that need to be converted + to memory references. The latter should be avoidable with proper + definition of patterns in machine description. We come here right + near the end of things, immediately before delayed branch + scheduling. */ + +void +c4x_process_after_reload (first) + rtx first; +{ + rtx operand0; + rtx insn; + int i; + + for (insn = first; insn; insn = NEXT_INSN (insn)) + { + /* Look for insn. */ + if (GET_RTX_CLASS (GET_CODE (insn)) == 'i') + { + int noperands; + int insn_code_number; + + insn_code_number = recog_memoized (insn); + + if (insn_code_number < 0) + continue; + + /* We split all insns here if they have a # for the output + template if we are using the big memory model since there + is a chance that we might be accessing memory across a + page boundary. */ + + if (!TARGET_SMALL) + { + 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; + + /* Do we have to update the basic block info here? + Maybe reorg wants it sorted out... */ + + /* Continue with the first of the new insns gnerated + by the split. */ + insn = new; + + insn_code_number = recog_memoized (insn); + + if (insn_code_number < 0) + continue; + } + } + + /* Ignore jumps and calls. */ + if (GET_CODE (insn) == CALL_INSN + || GET_CODE (insn) == JUMP_INSN) + { + continue; /* Hopefully we are not hosed here. */ + } + + noperands = insn_n_operands[insn_code_number]; + + insn_extract (insn); + + operand0 = recog_operand[0]; + + for (i = 0; i < noperands; i++) + if (c4x_scan_for_ldp (recog_operand_loc[i], insn, operand0)) + break; + } + } +} + + +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_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_float_constant (op) + rtx op; +{ + if (GET_CODE (op) != CONST_DOUBLE) + return 0; + return GET_MODE (op) == QFmode || GET_MODE (op) == HFmode; +} + + +int +c4x_H_constant (op) + rtx op; +{ + return c4x_float_constant (op) && c4x_immed_float_p (op); +} + + +int +c4x_I_constant (op) + rtx op; +{ + return c4x_int_constant (op) && IS_INT16_CONST (INTVAL (op)); +} + + +int +c4x_J_constant (op) + rtx op; +{ + if (TARGET_C3X) + return 0; + return c4x_int_constant (op) && IS_INT8_CONST (INTVAL (op)); +} + + +static int +c4x_K_constant (op) + rtx op; +{ + if (TARGET_C3X) + return 0; + return c4x_int_constant (op) && IS_INT5_CONST (INTVAL (op)); +} + + +int +c4x_L_constant (op) + rtx op; +{ + return c4x_int_constant (op) && IS_UINT16_CONST (INTVAL (op)); +} + + +static int +c4x_N_constant (op) + rtx op; +{ + return c4x_int_constant (op) && IS_NOT_UINT16_CONST (INTVAL (op)); +} + + +static int +c4x_O_constant (op) + rtx op; +{ + return c4x_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; +} + + +/* Symbol ref. */ + +int +c4x_T_constraint (op) + rtx op; +{ + if (GET_CODE (op) != MEM) + return 0; + op = XEXP (op, 0); + + if ((GET_CODE (op) == PLUS) + && (GET_CODE (XEXP (op, 0)) == REG) + && (REGNO (XEXP (op, 0)) == DP_REGNO)) + { + op = XEXP (op, 1); + } + else if ((GET_CODE (op) == PLUS) + && (GET_CODE (XEXP (op, 0)) == PLUS) + && (GET_CODE (XEXP (XEXP (op, 0), 0)) == USE)) + { + op = XEXP (op, 1); + } + else if ((GET_CODE (op) == PLUS) && (GET_CODE (XEXP (op, 0)) == USE)) + { + op = XEXP (op, 1); + } + + /* 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), 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; +{ + 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; + enum machine_mode mode; +{ + 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) != 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; +{ + return c4x_K_constant (op); +} + + +int +not_const_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + return c4x_N_constant (op); +} + + +int +reg_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + return register_operand (op, mode); +} + +int +reg_imm_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + if (REG_P (op) || CONSTANT_P (op)) + return 1; + return 0; +} + +int +not_modify_reg (op, mode) + rtx op; + enum machine_mode mode; +{ + 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 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; +{ + 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 (!register_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 (!register_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 (!register_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 (!register_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 (!register_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 (!register_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 (!register_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; +{ + return REG_P (op) && IS_DP_OR_PSEUDO_REGNO (op); +} + + +/* SP register. */ + +int +sp_reg_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + 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; +{ + return REG_P (op) && IS_ST_OR_PSEUDO_REGNO (op); +} + + +int +call_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + if (GET_CODE (op) != MEM) + return 0; + op = XEXP (op, 0); + switch (GET_CODE (op)) + { + case SYMBOL_REF: + case REG: + return 1; + default: + } + return 0; +} + + +/* Check src operand of two operand arithmetic instructions. */ + +int +src_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + if (REG_P (op)) + return reg_operand (op, mode); + + if (mode == VOIDmode) + mode = GET_MODE (op); + + /* We could allow certain CONST_INT values for HImode... */ + if (GET_CODE (op) == CONST_INT) + return (mode == QImode || mode == Pmode) && c4x_I_constant (op); + + /* We don't like CONST_DOUBLE integers. */ + if (GET_CODE (op) == CONST_DOUBLE) + return c4x_H_constant (op); + + 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 (REG_P (op)) + return reg_operand (op, mode); + + if (GET_CODE (op) == CONST_INT) + return c4x_L_constant (op) || c4x_J_constant (op); + + return general_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 (REG_P (op)) + return reg_operand (op, mode); + + if (GET_CODE (op) == CONST_INT) + return c4x_L_constant (op) || c4x_N_constant (op) || c4x_J_constant (op); + + return general_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; + + 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 (!TARGET_ALIASES) + { + if (MEM_VOLATILE_P (op0) && MEM_VOLATILE_P (op1)) + return 1; + } + else + { + 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; + + /* It is not worthwhile having parallel loads from the same address + unless we could be sure that both locations were in internal + memory. We allow this for peepholes (after reload has completed + since we are going to be executing two insns to the same address + anyhow) but steer the combiner away from doing this since it seems + to get the wrong idea. */ + if (!store0 && !store1 && base0 == base1 && disp0 == disp1 + && !reload_completed) + 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_operands_4 (operands, mode) + rtx *operands; + enum machine_mode mode; +{ + 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. */ + + /* 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; +} + +/* We only use this to check operands 1 and 2 since these may be + commutative. It will need extending for the C32 opcodes. */ +int +valid_parallel_operands_5 (operands, mode) + rtx *operands; + enum machine_mode mode; +{ + int regs = 0; + rtx op0 = operands[1]; + rtx op1 = operands[2]; + + if (GET_CODE (op0) == SUBREG) + op0 = SUBREG_REG (op0); + if (GET_CODE (op1) == SUBREG) + op1 = SUBREG_REG (op1); + + /* The patterns should only allow ext_low_reg_operand() or + par_ind_operand() operands. */ + + if (GET_CODE (op0) == REG) + regs++; + if (GET_CODE (op1) == REG) + regs++; + + return regs == 1; +} + + +int +valid_parallel_operands_6 (operands, mode) + rtx *operands; + enum machine_mode mode; +{ + int regs = 0; + rtx op0 = operands[1]; + rtx op1 = operands[2]; + rtx op2 = operands[4]; + rtx op3 = operands[5]; + + 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 input operands, only 2 + should be REGs and the other 2 should be MEMs. */ + + if (GET_CODE (op0) == REG) + regs++; + if (GET_CODE (op1) == REG) + regs++; + if (GET_CODE (op2) == REG) + regs++; + if (GET_CODE (op3) == REG) + regs++; + + /* The new C30/C40 silicon dies allow 3 regs of the 4 input operands. + Perhaps we should count the MEMs as well? */ + return regs == 2; +} + + +int +legitimize_parallel_operands_6 (operands, mode) + rtx *operands; + enum machine_mode mode; +{ + /* It's gonna be hard to legitimize operands for a parallel + instruction... TODO... */ + return valid_parallel_operands_6 (operands, mode); +} + + +/* 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 ("c4x_valid_operands: Internal error"); + 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: + + /* 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 + && !c4x_J_constant (operands[1]) + && INTVAL (operands[1]) != 0) + || GET_CODE (operands[1]) == CONST_DOUBLE)) + 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 + && !c4x_J_constant (operands[2]) + && INTVAL (operands[2]) != 0) + || GET_CODE (operands[2]) == CONST_DOUBLE))) + 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)); + + switch (code) + { + case POST_INC: + case PRE_INC: + if (mode == HImode) + mode = QImode; + else if (mode == HFmode) + mode = QFmode; + return gen_rtx (MEM, mode, 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); + + 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)); + 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); + TREE_TYPE (sect) = char_array_type_node; + 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; + tree identifier; + tree args; +{ + 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; +} + + +/* This is a modified version of modified_between_p that doesn't give + up if a changing MEM is found. It checks all insns between START + and END to see if any registers mentioned in X are set. */ +static int +c4x_modified_between_p (x, start, end) + rtx x; + rtx start, end; +{ + enum rtx_code code = GET_CODE (x); + char *fmt; + int i, j; + + switch (code) + { + case CONST_INT: + case CONST_DOUBLE: + case CONST: + case SYMBOL_REF: + case LABEL_REF: + return 0; + + case PC: + case CC0: + return 1; + + case MEM: + break; + + case REG: + return reg_set_between_p (x, start, end); + + default: + break; + } + + fmt = GET_RTX_FORMAT (code); + for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) + { + if (fmt[i] == 'e' && c4x_modified_between_p (XEXP (x, i), start, end)) + return 1; + + if (fmt[i] == 'E') + for (j = XVECLEN (x, i) - 1; j >= 0; j--) + if (c4x_modified_between_p (XVECEXP (x, i, j), start, end)) + return 1; + } + + return 0; +} + +/* Return 1 if rtx X references memory that is changing. */ +static int +c4x_mem_ref_p (x) + rtx x; +{ + enum rtx_code code = GET_CODE (x); + char *fmt; + int i, j; + + if (code == MEM && !RTX_UNCHANGING_P (x)) + return 1; + + fmt = GET_RTX_FORMAT (code); + for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) + { + if (fmt[i] == 'e' && c4x_mem_ref_p (XEXP (x, i))) + return 1; + + if (fmt[i] == 'E') + for (j = XVECLEN (x, i) - 1; j >= 0; j--) + if (c4x_mem_ref_p (XVECEXP (x, i, j))) + return 1; + } + + return 0; +} + +/* Return 1 if rtx X sets or clobbers memory. */ +static int +c4x_mem_set_p (x) + rtx x; +{ + enum rtx_code code = GET_CODE (x); + char *fmt; + int i, j; + + if ((code == SET || code == CLOBBER) + && (GET_CODE (SET_DEST (x)) == MEM)) + return 1; + + fmt = GET_RTX_FORMAT (code); + for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) + { + if (fmt[i] == 'e' && c4x_mem_set_p (XEXP (x, i))) + return 1; + + if (fmt[i] == 'E') + for (j = XVECLEN (x, i) - 1; j >= 0; j--) + if (c4x_mem_set_p (XVECEXP (x, i, j))) + return 1; + } + + return 0; +} + + +/* Return 1 if any insns between START and END (exclusive) sets + or clobbers memory. */ +static int +c4x_mem_modified_between_p (start, end) + rtx start, end; +{ + rtx insn; + + if (start == end) + return 0; + + for (insn = NEXT_INSN (start); insn != end; insn = NEXT_INSN (insn)) + if (GET_RTX_CLASS (GET_CODE (insn)) == 'i' + && c4x_mem_set_p (PATTERN (insn))) + return 1; + return 0; +} + + +/* Returns 1 if INSN can be moved past all the insns between START and + END exclusive. If TARGET_ALIASES is not set and a memory store is + detected, then 0 is returned. */ +static int +c4x_insn_moveable_p (insn, start, end) + rtx insn; + rtx start, end; +{ + if (start == end) + return 1; + + /* We can't use modified_between_p since this will + return 1 if set1 contains a MEM. */ + if (c4x_modified_between_p (insn, start, end)) + return 0; + + return 1; +} + + +/* See if the insns INSN1 and INSN2 can be packed into a PARALLEL. + Return 0 if the insns cannot be packed or the rtx of the packed + insn (with clobbers added as necessary). If DEPEND is non zero, + then the destination register of INSN1 must be used by INSN2. */ +static rtx +c4x_parallel_pack (insn1, insn2, depend) + rtx insn1; + rtx insn2; + int depend; +{ + rtx set1; + rtx set2; + rtx pack; + enum machine_mode mode1; + enum machine_mode mode2; + int num_clobbers; + int insn_code_number; + + /* We could generalise things to not just rely on single sets. */ + if (!(set1 = single_set (insn1)) + || !(set2 = single_set (insn2))) + return 0; + + mode1 = GET_MODE (SET_DEST (set1)); + mode2 = GET_MODE (SET_DEST (set2)); + if (mode1 != mode2) + return 0; + + if (depend) + { + rtx dst1; + + /* Require insn2 to be dependent upon the result of insn1. */ + dst1 = SET_DEST (set1); + + if (!REG_P (dst1)) + return 0; + + if (!reg_mentioned_p (dst1, set2)) + return 0; + + /* The dependent register must die in insn2 since a parallel + insn will generate a new value. */ + if (!find_regno_note (insn2, REG_DEAD, REGNO (dst1))) + return 0; + } + + pack = gen_rtx (PARALLEL, VOIDmode, gen_rtvec (2, set1, set2)); + num_clobbers = 0; + if ((insn_code_number = recog (pack, pack, &num_clobbers)) < 0) + return 0; + + if (num_clobbers != 0) + { + rtx newpack; + int i; + + newpack = gen_rtx (PARALLEL, VOIDmode, + gen_rtvec (GET_CODE (pack) == PARALLEL + ? XVECLEN (pack, 0) + num_clobbers + : num_clobbers + 1)); + + if (GET_CODE (pack) == PARALLEL) + for (i = 0; i < XVECLEN (pack, 0); i++) + XVECEXP (newpack, 0, i) = XVECEXP (pack, 0, i); + else + XVECEXP (newpack, 0, 0) = pack; + + add_clobbers (newpack, insn_code_number); + pack = newpack; + } + + return pack; +} + + +static rtx +c4x_parallel_find (insn1, loop_end, depend, insn2) + rtx insn1; + rtx loop_end; + int depend; + rtx *insn2; +{ + rtx insn; + rtx pack; + + /* We could use the logical links if depend is non zero? */ + + for (insn = NEXT_INSN (insn1); insn != loop_end; insn = NEXT_INSN(insn)) + { + switch (GET_CODE (insn)) + { + default: + case JUMP_INSN: + case CALL_INSN: + case NOTE: + break; + + case INSN: + if (!(pack = c4x_parallel_pack (insn1, insn, depend))) + break; + + /* What if insn1 or insn2 sets cc and is required by another + insn? */ + +#if 0 + /* Check that nothing between insn1 and insn will spoil the + show. */ + if (NEXT_INSN (insn1) != insn + && c4x_modified_between_p (insn, NEXT_INSN (insn1), insn)) + return 0; +#else + /* This will do in the interim. If the insns between + insn1 and insn are harmless, we can move things around + if we're careful. */ + if (next_nonnote_insn (insn1) != insn) + return 0; +#endif + + /* Do some checks here... */ + *insn2 = insn; + return pack; + } + } + return 0; +} + + +/* Update the register info for reg REG found in the basic block BB, + where SET is 1 if the register is being set. */ +static void +c4x_update_info_reg (reg, set, bb) + rtx reg; + int set; + int bb; +{ + int regno; + + if (!REG_P (reg)) + fatal_insn ("Expecting register rtx", reg); + + regno = REGNO (reg); + + /* REGNO_FIRST_UID and REGNO_LAST_UID don't need setting. */ + + SET_REGNO_REG_SET (basic_block_live_at_start[bb], regno); + REG_BASIC_BLOCK (regno) = REG_BLOCK_GLOBAL; + if (set) + REG_N_SETS (regno)++; + else + REG_N_REFS (regno)++; +} + + +/* Update the register info for all the regs in X found in the basic + block BB. */ +static void +c4x_update_info_regs(x, bb) + rtx x; + int bb; +{ + enum rtx_code code; + char *fmt; + int i, j; + + if (!x) + return; + + code = GET_CODE (x); + switch (code) + { + case CLOBBER: +#if 0 + if (REG_P (SET_DEST (x))) + return; + break; +#endif + + case SET: + if (REG_P (SET_DEST (x))) + c4x_update_info_reg (SET_DEST (x), 1, bb); + else + c4x_update_info_regs (SET_DEST (x), bb); + + if (code == SET) + c4x_update_info_regs (SET_SRC (x), bb); + return; + + case REG: + c4x_update_info_reg (x, 0, bb); + return; + + default: + break; + } + + fmt = GET_RTX_FORMAT (code); + for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) + { + if (fmt[i] == 'e') + c4x_update_info_regs (XEXP (x, i), bb); + else if (fmt[i] == 'E') + for (j = XVECLEN (x, i) - 1; j >= 0; j--) + c4x_update_info_regs (XVECEXP (x, i, j), bb); + } +} + + +static void +c4x_copy_insn_after(insn, prev, bb) + rtx insn; + rtx prev; + int bb; +{ + rtx note; + rtx new; + + emit_insn_after (copy_rtx (PATTERN (insn)), prev); + + new = NEXT_INSN (prev); + + /* Copy the REG_NOTES from insn to the new insn. */ + for (note = REG_NOTES (insn); note; note = XEXP (note, 1)) + REG_NOTES (new) = gen_rtx (GET_CODE (note), + REG_NOTE_KIND (note), + XEXP (note, 0), + REG_NOTES (new)); + + /* Handle all the registers within insn and update the reg info. */ + c4x_update_info_regs (PATTERN (insn), bb); +} + + +static void +c4x_copy_insns_after(start, end, pprev, bb) + rtx start; + rtx end; + rtx *pprev; + int bb; +{ + rtx insn; + + for (insn = start; insn != NEXT_INSN (end); insn = NEXT_INSN(insn)) + { + switch (GET_CODE (insn)) + { + case CALL_INSN: + /* We could allow a libcall with no side effects??? */ + fatal_insn("Repeat block loop contains a call", insn); + break; + + case INSN: + c4x_copy_insn_after(insn, *pprev, bb - 1); + *pprev = NEXT_INSN (*pprev); + break; + + default: + break; + } + } +} + + +/* Merge the notes of insn2 with the notes of insn. */ +static void +c4x_merge_notes(insn, insn2) + rtx insn; + rtx insn2; +{ + rtx note; + + for (note = REG_NOTES (insn2); note; note = XEXP (note, 1)) + { + rtx link; + + for (link = REG_NOTES (insn); link; link = XEXP (link, 1)) + if (REG_NOTE_KIND (note) == REG_NOTE_KIND (link) + && XEXP (note, 0) == XEXP (link, 0)) + remove_note (insn, note); + } + for (note = REG_NOTES (insn2); note; note = XEXP (note, 1)) + REG_NOTES (insn) = gen_rtx (GET_CODE (note), + REG_NOTE_KIND (note), + XEXP (note, 0), + REG_NOTES (insn)); +} + + +/* This pass must update information that subsequent passes expect to be + correct. Namely: reg_n_refs, reg_n_sets, reg_n_deaths, + reg_n_calls_crossed, and reg_live_length. Also, basic_block_head, + basic_block_end. */ + +static int +c4x_parallel_process (loop_start, loop_end) + rtx loop_start; + rtx loop_end; +{ + rtx insn; + rtx insn2; + rtx pack; + rtx hoist; + rtx sink; + rtx loop_count; + rtx loop_count_set; + rtx end_label; + int num_packs; + int bb; + + /* The loop must have a calculable number of iterations + since we need to reduce the loop count by one. + + For now, only process repeat block loops, since we can tell that + these have a calculable number of iterations. + + The loop count must be at least 2? */ + + loop_count = NEXT_INSN (loop_start); + + /* Skip past CLOBBER and USE and deleted insn. This is from flow. */ + for (;;) + { + if (GET_CODE (loop_count) == INSN) + { + rtx x = PATTERN (loop_count); + if (GET_CODE (x) != USE && GET_CODE (x) != CLOBBER) + break; + } + else if (GET_CODE (loop_count) == NOTE) + { + if (! INSN_DELETED_P (loop_count)) + break; + } + else + break; + loop_count = NEXT_INSN (loop_count); + } + + if (!(loop_count_set = single_set (loop_count))) + return 0; + + if (!REG_P (SET_DEST (loop_count_set)) + || REGNO (SET_DEST (loop_count_set)) != RC_REGNO) + return 0; + + /* Determine places to hoist and sink insns out of the loop. We + won't have to update basic_block_head if we move things after + loop_count. */ + + hoist = loop_count; + end_label = PREV_INSN (loop_end); + + /* Skip past filler insn if present. */ + if (GET_CODE (end_label) != CODE_LABEL) + end_label = PREV_INSN (end_label); + + /* Skip past CLOBBER, USE, and deleted insns inserted by the flow pass. */ + for (;;) + { + if (GET_CODE (end_label) == INSN) + { + rtx x = PATTERN (end_label); + if (GET_CODE (x) != USE && GET_CODE (x) != CLOBBER) + break; + } + else if (GET_CODE (end_label) == NOTE) + { + if (! INSN_DELETED_P (end_label)) + break; + } + else + break; + end_label = PREV_INSN (end_label); + } + + if (GET_CODE (end_label) != CODE_LABEL) + return 0; + + sink = end_label; + + /* There must be an easier way to work out which basic block we are + in. */ + for (bb = 0; bb < n_basic_blocks; bb++) + if (basic_block_head[bb] == sink) + break; + + if (bb >= n_basic_blocks) + fatal_insn("Cannot find basic block for insn", sink); + + /* Skip to label at top of loop. */ + for (; GET_CODE (loop_start) != CODE_LABEL; + loop_start = NEXT_INSN(loop_start)); + + num_packs = 0; + for (insn = loop_start; insn != loop_end; insn = NEXT_INSN(insn)) + { + switch (GET_CODE (insn)) + { + default: + case JUMP_INSN: + case CALL_INSN: + case NOTE: + break; + + case INSN: + + /* Look for potential insns to combine where the second one + is dependent upon the first. We could have another pass + that tries combining independent insns but that is not so + important. We could do this afterwards as a more generic + peepholer. */ + + if ((pack = c4x_parallel_find(insn, loop_end, 1, &insn2))) + { + rtx set1; + rtx set2; + rtx note; + rtx seq_start; + + set1 = single_set (insn); + set2 = single_set (insn2); + + /* We need to hoist a copy of insn1 out of the loop and + to sink a copy insn2 out of the loop. We can avoid + the latter if the destination of insn2 is used + by a following insn within the loop. + + We cannot hoist insn1 out of the loop if any of the + preceeding insns within the loop modifies the destination + of insn1 or modifies any of the operands of insn1. */ + + /* If the user has flagged that there are potential aliases, + then we can't move the insn if it references memory + past any insns that modify memory. */ + if (TARGET_ALIASES + && c4x_mem_ref_p (PATTERN (insn)) + && c4x_mem_modified_between_p (loop_start, loop_end)) + break; + + /* None of the registers used in insn can be modified by + any of the insns from the start of the loop until insn. */ + if (!c4x_insn_moveable_p (set1, loop_start, insn)) + break; + + /* None of the registers used in insn can be modified by + any of the insns after insn2 until the end of the + loop, especially the result which needs to be saved + for the next iteration. */ + if (!c4x_insn_moveable_p (set1, insn2, loop_end)) + break; + + /* We need to hoist all the insns from the loop top + to and including insn. */ + c4x_copy_insns_after(NEXT_INSN (loop_start), insn, &hoist, bb); + + /* We need to sink all the insns after insn to + loop_end. */ + c4x_copy_insns_after (NEXT_INSN (insn), PREV_INSN(end_label), + &sink, bb + 1); + + /* Change insn to the new parallel insn, retaining the notes + of the old insn. */ + if (!validate_change (insn, &PATTERN (insn), pack, 0)) + fatal_insn("Cannot replace insn with parallel insn", pack); + + /* Copy the REG_NOTES from insn2 to the new insn + avoiding duplicates. */ + c4x_merge_notes (insn, insn2); + + delete_insn (insn2); + + /* The destination register of insn1 no longer dies in + this composite insn. Don't use remove_death since that + alters REG_N_DEATHS. The REG_DEAD note has just been + moved. */ + note = find_regno_note (insn, REG_DEAD, REGNO (SET_DEST (set1))); + if (note) + remove_note (insn, note); + + /* Do we have to modify the LOG_LINKS? */ + + /* We need to decrement the loop count. We probably + should test if RC is negative and branch to end label + if so. */ + if (GET_CODE (SET_SRC (loop_count_set)) == CONST_INT) + { + /* The loop count must be more than 1 surely? */ + SET_SRC (loop_count_set) + = gen_rtx (CONST_INT, VOIDmode, + INTVAL (SET_SRC (loop_count_set)) -1); + } + else if (GET_CODE (SET_SRC (loop_count_set)) == PLUS + && GET_CODE (XEXP (SET_SRC (loop_count_set), 1)) + == CONST_INT) + { + XEXP (SET_SRC (loop_count_set), 1) + = gen_rtx (CONST_INT, VOIDmode, + INTVAL (XEXP (SET_SRC (loop_count_set), 1)) + - 1); + } + else + { + start_sequence (); + expand_binop (QImode, sub_optab, + gen_rtx (REG, QImode, RC_REGNO), + gen_rtx (CONST_INT, VOIDmode, 1), + gen_rtx (REG, QImode, RC_REGNO), + 1, OPTAB_DIRECT); + seq_start = get_insns (); + end_sequence (); + emit_insns_after (seq_start, loop_count); + + /* Check this. What if we emit more than one insn? + Can we emit more than one insn? */ + REG_NOTES (seq_start) + = gen_rtx (EXPR_LIST, REG_UNUSED, + gen_rtx (REG, QImode, RC_REGNO), + REG_NOTES (seq_start)); + } + + start_sequence (); + emit_cmp_insn (gen_rtx (REG, QImode, RC_REGNO), + const0_rtx, LT, NULL_RTX, QImode, 0, 0); + emit_jump_insn (gen_blt (end_label)); + seq_start = get_insns (); + end_sequence (); + emit_insns_after (seq_start, hoist); + + /* This is a bit of a hack... */ + REG_NOTES (NEXT_INSN (seq_start)) + = gen_rtx (EXPR_LIST, REG_DEAD, + gen_rtx (REG, QImode, RC_REGNO), + REG_NOTES (NEXT_INSN (seq_start))); + + if (TARGET_DEVEL) + debug_rtx(insn); + + num_packs ++; + +#if 1 + /* If we want to pack more than one parallel insn + we will have to tag which insns have been + hoisted/sunk/paired. We might need a recursive approach. */ + + return num_packs; +#endif + } + break; + } + } + return num_packs; +} + + +static void +c4x_combine_parallel_independent (insns) + rtx insns; +{ + /* Combine independent insns like + (set (mem (reg 0)) (reg 1)) + (set (reg 2) (mem (reg 3))) + where (reg 1) != (reg 2) unless there is a REG_DEAD note + on the first insn. */ + +} + +static void +c4x_combine_parallel_dependent (insns) + rtx insns; +{ + rtx insn; + rtx loop_start; + rtx loop_end; + int num_jumps; + int num_insns; + + /* Find the innermost loop and check that it is unjumped. */ + loop_start = NULL_RTX; + num_jumps = 0; + for (insn = insns; insn; insn = NEXT_INSN(insn)) + { + switch (GET_CODE (insn)) + { + case INSN: + num_insns++; + break; + + case CALL_INSN: + /* We could allow a libcall with no side effects??? */ + case JUMP_INSN: + num_jumps++; + break; + + case NOTE: + switch (NOTE_LINE_NUMBER (insn)) + { + case NOTE_INSN_LOOP_BEG: + loop_start = insn; + num_jumps = 0; + num_insns = 0; + break; + + case NOTE_INSN_LOOP_CONT: + if (!loop_start) + break; + /* We can't handle a loop with jumps or calls. + If there are too many insns, we are unlikely + to be able to find a suitable case for optimisation. + The maximum number of insns may require tweaking. */ + if (!num_jumps && num_insns < 20) + { + /* Skip to end of loop. */ + loop_end = NULL_RTX; + for (; insn; insn = NEXT_INSN(insn)) + if (GET_CODE (insn) == NOTE + && NOTE_LINE_NUMBER (insn) == NOTE_INSN_LOOP_END) + break; + loop_end = insn; + if (!loop_end) + fatal_insn("Could not find note at end of loop", + loop_start); + c4x_parallel_process(loop_start, loop_end); + } + loop_start = NULL_RTX; + break; + + default: + break; + } + default: + break; + } + } +} + + +void +c4x_combine_parallel (insns) + rtx insns; +{ + /* Only let people who know how to shoot themselves in the foot do so! */ + if (!TARGET_PARALLEL_PACK) + return; + + c4x_combine_parallel_dependent (insns); + + c4x_combine_parallel_independent (insns); +} + + +/* True if INSN is between START and END. If END precedes START + something has gone awry. */ + +static int +c4x_rptb_in_range (insn, start, end) + rtx insn, start, end; +{ + rtx this; + + for (this = start; ; this = NEXT_INSN (this)) + { + if (this == insn) + return 1; + if (this == end) + return 0; + if (this == NULL_RTX) + fatal_insn ("c4x_rptb_in_range: Repeat block error", start); + } +} + + +/* Returns true if there are no jumps crossing the loop boundary and + no calls anywhere. */ + +int +c4x_rptb_unjumped_loop_p (loop_start, loop_end) + rtx loop_start, loop_end; +{ + rtx insn; + rtx continue_label = NULL_RTX; + rtx continue_note = NULL_RTX; /* Loop continue note if there is one. */ + + /* Scan loop backwards. */ + for (insn = PREV_INSN (loop_end); insn && insn != loop_start; + insn = PREV_INSN (insn)) + { + switch (GET_CODE (insn)) + { + case JUMP_INSN: + { + rtx jump_label = JUMP_LABEL (insn); + + /* We don't like jumps out of the loop. We also look + for jumps to the end of loop, say from a continue + statement. */ + if (continue_note + && jump_label == next_nonnote_insn (continue_note)) + continue_label = jump_label; + else if (!c4x_rptb_in_range (jump_label, loop_start, + continue_note ? continue_note : + loop_end)) + return 0; + } + /* Fall through */ + + case INSN: + if (0 && volatile_refs_p (PATTERN (insn))) + { + c4x_dump (loop_dump_stream, + "Repeat block: Volatile memory ref within loop\n"); + return 0; + } + + /* The C4x movstrqi_large pattern clobbers RC, RE, RS. + This should be generalised to check for insns that use + these registers within the loop. */ + if (recog_memoized (insn) == CODE_FOR_movstrqi_large) + { + c4x_dump (loop_dump_stream, + "Repeat block: Memory copy within loop\n"); + return 0; + } + break; + + /* It is not worthwhile preserving the zero overhead loop + context across calls. */ + case CALL_INSN: + /* We could allow a libcall with no side effects??? */ + c4x_dump (loop_dump_stream, "Repeat block: Call within loop\n"); + return 0; + + case NOTE: + switch (NOTE_LINE_NUMBER (insn)) + { + case NOTE_INSN_LOOP_CONT: + if (continue_note == NULL_RTX) + continue_note = insn; + + /* Check for empty loop which would throw c4x_rptb_nop_p. + GCC doesn't optimise empty loops away since user + may be trying to implement a simple but crude delay. */ + if (GET_CODE (PREV_INSN (insn)) == NOTE + && NOTE_LINE_NUMBER (PREV_INSN (insn)) == NOTE_INSN_LOOP_BEG) + { + c4x_dump (loop_dump_stream, "Repeat block: Empty loop\n"); + return 0; + } + break; + + /* If we find a LOOP_END note, then we are not in the + innermost loop. */ + case NOTE_INSN_LOOP_END: + return 0; + + default: + continue; + } + default: + continue; + } + } + if (insn == NULL_RTX) + fatal("Repeat block: Inconsistent loop"); + + c4x_dump (loop_dump_stream, "Repeat block: Unjumped loop\n"); + if (continue_label) + c4x_dump (loop_dump_stream, "Repeat block: Continue_label %d\n", + INSN_UID (continue_label)); + return 1; +} + + +/* Find and record in PCOMP and PJUMP the final comparison and jump + insns of the loop specified by LOOP_END. Return 1 if both have been + found, otherwise return 0. */ + +static int +c4x_rptb_find_comp_and_jump (loop_end, pcomp, pjump) + rtx loop_end; + rtx *pcomp, *pjump; +{ + rtx final_comp, comp_pat; + rtx final_jump = prev_nonnote_insn (loop_end); + + if (!final_jump) + return 0; + + final_comp = PREV_INSN (final_jump); + if (!final_comp) + return 0; + + if ((GET_CODE (final_comp) != INSN)) + return 0; + + comp_pat = PATTERN (final_comp); + + if ((GET_CODE (comp_pat) != SET) + || GET_CODE (XEXP (comp_pat, 0)) != REG + || REGNO (XEXP (comp_pat, 0)) != ST_REGNO) + return 0; + + *pcomp = final_comp; + *pjump = final_jump; + return 1; +} + + +/* Determine if the loop count is computable for a repeat loop. */ + +static int +c4x_rptb_loop_info_get (loop_start, loop_end, loop_info) + rtx loop_start, loop_end; + c4x_rptb_info_t *loop_info; +{ + rtx iteration_var, initial_value, increment, comparison; + enum rtx_code cc; /* Comparison code */ + rtx comparison_value; + + loop_info->loop_start = loop_start; + loop_info->loop_count = loop_iterations (loop_start, loop_end); + + /* If the number of loop cycles does not need calculating at + run-time then things are easy... Note that the repeat count + value must be a positive integer for the RPTB instruction. If + loop_count is zero then we don't have a constant count. */ + if (loop_info->loop_count > 0) + return 1; + if (loop_info->loop_count < 0) + { + c4x_dump (loop_dump_stream, "Repeat block: Negative loop count %d\n", + loop_info->loop_count); + return 0; + } + + comparison = get_condition_for_loop (prev_nonnote_insn (loop_end)); + if (comparison == NULL_RTX) + { + c4x_dump (loop_dump_stream, "Repeat block: Cannot find comparison\n"); + return 0; + } + cc = GET_CODE (comparison); + + /* Only allow a register as the iteration value. */ + iteration_var = XEXP (comparison, 0); + if (GET_CODE (iteration_var) != REG) + { + c4x_dump (loop_dump_stream, "Repeat block: Non reg. iteration value\n"); + return 0; + } + + c4x_dump (loop_dump_stream, "Repeat block: Iteration value regno = %d\n", + REGNO (iteration_var)); + + /* The comparison value must not change on the fly. */ + comparison_value = XEXP (comparison, 1); + if (!invariant_p (comparison_value)) + { + c4x_dump (loop_dump_stream, "Repeat block: Comparison value variant\n"); + return 0; + } + + /* This routine in unroll.c does the hard work of finding the + initial value and increment for us. Currently it won't find the + intitial value or increment for do {} while; or while() {} do; + loops. This is because the iteration_var we find in the + comparison insn is a GIV rather than a BIV and iteration_info does + not like GIVs. We could scan all the BIVs like check_dbra_loop() + does... */ + + iteration_info (iteration_var, &initial_value, &increment, + loop_start, loop_end); + if (initial_value == NULL_RTX || increment == NULL_RTX) + { + c4x_dump (loop_dump_stream, "Repeat block: Cannot determine initial" + " value or increment\n"); + return 0; + } + + /* Only allow constant integer increment, not a variable. */ + if (GET_CODE (increment) != CONST_INT) + { + c4x_dump (loop_dump_stream, "Repeat block: Increment not constant\n"); + return 0; + } + + loop_info->incr = INTVAL (increment); + + /* If the increment is not a power of 2, (i.e, 1, 2, 4, etc.) then + we will need to emit a divide instruction rather than a right + shift to calculate the loop count. */ + if ((loop_info->shift = exact_log2 (abs (loop_info->incr))) < 0) + { + c4x_dump (loop_dump_stream, "Repeat block: Increment not power of 2\n"); + return 0; + } + + /* The front end changes GT to NE for unsigned numbers, so we + "undo" this here for clarity. */ + loop_info->unsigned_p = 0; + if (GET_CODE (increment) == CONST_INT + && INTVAL (increment) == -1 && cc == NE) + { + loop_info->unsigned_p = 1; + cc = GT; + } + + if (!(cc == LT || cc == LE || cc == LTU || cc == LEU + || cc == GT || cc == GE || cc == GTU || cc == GEU)) + { + c4x_dump (loop_dump_stream, "Repeat block: Invalid comparison\n"); + return 0; + } + + loop_info->swap_p = (cc == GT || cc == GE || cc == GTU || cc == GEU); + if (loop_info->swap_p) + { + loop_info->start_value = comparison_value; + loop_info->end_value = initial_value; + loop_info->incr = -loop_info->incr; + } + else + { + loop_info->start_value = initial_value; + loop_info->end_value = comparison_value; + } + + /* Check if loop won't terminate? */ + if (loop_info->incr <= 0) + { + c4x_dump (loop_dump_stream, "Repeat block: Increment negative\n"); + return 0; + } + + loop_info->off_by_one = (cc == LT || cc == LTU || cc == GT || cc == GTU); + loop_info->unsigned_p |= (cc == LTU || cc == LEU || cc == GTU || cc == GEU); + + /* We have a switch to allow an unsigned loop counter. + We'll normally disallow this case since the the repeat + count for the RPTB instruction must be less than 0x80000000. */ + if (loop_info->unsigned_p && !TARGET_LOOP_UNSIGNED) + { + c4x_dump (loop_dump_stream, "Repeat block: Unsigned comparison\n"); + return 0; + } + + return 1; +} + + +/* Emit insn(s) to compute loop iteration count. */ + +static rtx +c4x_rptb_emit_init (loop_info) + c4x_rptb_info_t *loop_info; +{ + rtx result; + int adjust; + rtx seq_start; + + /* If have a known constant loop count, things are easy... */ + if (loop_info->loop_count > 0) + return gen_rtx (CONST_INT, VOIDmode, loop_info->loop_count - 1); + + if (loop_info->shift < 0) + abort (); + + start_sequence (); + + result = loop_info->end_value; + if (loop_info->start_value != const0_rtx) + { + /* end_value - start_value */ + result = expand_binop (QImode, sub_optab, + result, loop_info->start_value, + 0, loop_info->unsigned_p, OPTAB_DIRECT); + } + + adjust = loop_info->incr - loop_info->off_by_one; + if (adjust > 0) + { + /* end_value - start_value + adjust */ + result = expand_binop (QImode, add_optab, + result, GEN_INT (adjust), + 0, loop_info->unsigned_p, OPTAB_DIRECT); + } + + if (loop_info->shift > 0) + { + /* (end_value - start_value + adjust) >> shift */ + result = expand_binop (QImode, loop_info->unsigned_p ? + lshr_optab : ashr_optab, result, + gen_rtx (CONST_INT, VOIDmode, + loop_info->shift), + 0, loop_info->unsigned_p, OPTAB_DIRECT); + } + + /* ((end_value - start_value + adjust) >> shift) - 1 */ + result = expand_binop (QImode, sub_optab, + result, gen_rtx (CONST_INT, VOIDmode, 1), + 0, loop_info->unsigned_p, OPTAB_DIRECT); + + seq_start = get_insns (); + end_sequence (); + + emit_insns_before (seq_start, loop_info->loop_start); + return result; +} + + +/* This routine checks for suitable loops that can use zero overhead + looping and emits insns marking the start and end of the loop + as well as an insn for initialising the loop counter. */ + +void +c4x_rptb_process (loop_start, loop_end) + rtx loop_start, loop_end; +{ + rtx iteration_count; + rtx start_label; + rtx end_label; + rtx comp_insn; + rtx jump_insn; + c4x_rptb_info_t info; + + if (!TARGET_RPTB) + return; + + /* Check that there are no jumps crossing loop boundary or calls. */ + if (!c4x_rptb_unjumped_loop_p (loop_start, loop_end)) + return; + + start_label = next_nonnote_insn (loop_start); + if (GET_CODE (start_label) != CODE_LABEL) + return; + + /* Find comparison and jump insns. */ + if (!c4x_rptb_find_comp_and_jump (loop_end, &comp_insn, &jump_insn)) + return; + + /* If we don't jump back to start label, then the loop is no good. */ + if (start_label != JUMP_LABEL (jump_insn)) + return; + + /* Check that number of loops is computable. */ + if (!c4x_rptb_loop_info_get (loop_start, loop_end, &info)) + return; + + c4x_dump (loop_dump_stream, "Repeat block: Loop start at %d, end at %d\n", + INSN_UID (loop_start), INSN_UID (loop_end)); + + if (info.loop_count > 0) + c4x_dump (loop_dump_stream, "Repeat block: Loop count = %d\n", + info.loop_count); + else + c4x_dump (loop_dump_stream, + "Repeat block: incr %d, shift %d, swap_p %d," + " off_by_one %d, unsigned_p %d\n", + info.incr, info.shift, info.swap_p, + info.off_by_one, info.unsigned_p); + + /* Emit insns to compute loop iteration count. */ + iteration_count = c4x_rptb_emit_init (&info); + if (iteration_count == NULL_RTX) + abort (); + + /* Add label at end of loop, immediately after jump insn. */ + end_label = gen_label_rtx (); + emit_label_after (end_label, jump_insn); + + /* Add label to forced label list to prevent jump optimisation + coalescing end_label with bypass_label since we need these destinct if + we are to sink insns out of the loop. */ + if (GET_CODE (NEXT_INSN (loop_end)) == CODE_LABEL) + { + rtx bypass_label; + + bypass_label = NEXT_INSN (loop_end); +#if 0 + forced_labels = gen_rtx (EXPR_LIST, VOIDmode, + end_label, forced_labels); + forced_labels = gen_rtx (EXPR_LIST, VOIDmode, + bypass_label, forced_labels); +#endif + emit_insn_after (gen_repeat_block_filler (), end_label); + + c4x_dump (loop_dump_stream, + "Repeat block: Start label at %d, end label at %d," + " bypass label at %d\n", + INSN_UID (start_label), INSN_UID (end_label), + INSN_UID (bypass_label)); + } + else + { + emit_insn_after (gen_repeat_block_filler (), end_label); + c4x_dump (loop_dump_stream, + "Repeat block: Start label at %d, end label at %d\n", + INSN_UID (start_label), INSN_UID (end_label)); + } + + /* Create pattern for repeat_block_top and insert at top of loop. */ + emit_insn_before (gen_repeat_block_top (const0_rtx, iteration_count, + start_label, end_label), + start_label); + + /* Replace the jump instruction with repeat_block_end insn. */ + PATTERN (jump_insn) = gen_repeat_block_end (const0_rtx, start_label); + + /* The insn is unrecognizable after the surgery. */ + INSN_CODE (jump_insn) = -1; + + /* Delete the comparison insn. */ + delete_insn (comp_insn); +} + + +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_repeat_block_end) + return 0; + + if (TARGET_RPTS) + return 1; + + return (GET_CODE (op) == CONST_INT) && TARGET_RPTS_CYCLES (INTVAL (op)); +} + +/* + Loop structure of `for' loops: + + Check if iterations required + If not, jump to BYPASS_LABEL + + NOTE_INSN_LOOP_BEG + <<> + START_LABEL: + {NOTE_BLOCK_BEGIN} + + Body of loop + + {NOTE_BLOCK_END} + {NOTE_INSN_LOOP_CONT} + + Increment loop counters here + + {NOTE_INSN_LOOP_VTOP} + <<>> + Exit test here <<>> + If not exiting jump to START_LABEL <<>> + <<> + + NOTE_INSN_LOOP_END + + BYPASS_LABEL: + + Note that NOTE_INSN_LOOP_VTOP is only required for loops such as + for loops, where it necessary to duplicate the exit test. This + position becomes another virtual start of the loop when considering + invariants. + + Note that if there is nothing in the loop body we get: + + NOTE_INSN_LOOP_BEG + NOTE_INSN_LOOP_CONT + START_LABEL: + NOTE_INSN_LOOP_VTOP + ... + */ + + +/* 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/gcc/config/c4x/c4x.h b/gcc/config/c4x/c4x.h new file mode 100644 index 0000000..cc92b2a --- /dev/null +++ b/gcc/config/c4x/c4x.h @@ -0,0 +1,2608 @@ +/* Definitions of target machine for GNU compiler. TMS320C[34]x + Copyright (C) 1994, 1995, 1996, 1997 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 pairs in braces, + each pair being { "NAME", VALUE } + 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 }, \ + { "big", -SMALL_MEMORY_FLAG }, \ + { "mpyi", MPYI_FLAG}, \ + { "no-mpyi", -MPYI_FLAG}, \ + { "fast-fix", FAST_FIX_FLAG}, \ + { "no-fast-fix", -FAST_FIX_FLAG}, \ + { "rpts", RPTS_FLAG}, \ + { "no-rpts", -RPTS_FLAG}, \ + { "rptb", RPTB_FLAG}, \ + { "no-rptb", -RPTB_FLAG}, \ + { "30", C30_FLAG}, \ + { "31", C31_FLAG}, \ + { "32", C32_FLAG}, \ + { "40", C40_FLAG}, \ + { "44", C44_FLAG}, \ + { "ti", TI_FLAG}, \ + { "no-ti", -TI_FLAG}, \ + { "paranoid", PARANOID_FLAG}, \ + { "no-paranoid", -PARANOID_FLAG}, \ + { "isr-dp-reload", PARANOID_FLAG}, \ + { "no-isr-dp-reload", -PARANOID_FLAG}, \ + { "memparm", MEMPARM_FLAG}, \ + { "regparm", -MEMPARM_FLAG}, \ + { "devel", DEVEL_FLAG}, \ + { "no-devel", -DEVEL_FLAG}, \ + { "bk", BK_FLAG}, \ + { "no-bk", -BK_FLAG}, \ + { "db", DB_FLAG}, \ + { "no-db", -DB_FLAG}, \ + { "debug", DEBUG_FLAG}, \ + { "no-debug", -DEBUG_FLAG}, \ + { "hoist", HOIST_FLAG}, \ + { "no-hoist", -HOIST_FLAG}, \ + { "no-force", -FORCE_FLAG}, \ + { "force", FORCE_FLAG}, \ + { "loop-unsigned", LOOP_UNSIGNED_FLAG}, \ + { "no-loop-unsigned", -LOOP_UNSIGNED_FLAG}, \ + { "preserve-float", PRESERVE_FLOAT_FLAG}, \ + { "no-preserve-float", -PRESERVE_FLOAT_FLAG}, \ + { "parallel-insns", PARALLEL_PACK_FLAG}, \ + { "no-parallel-mpy", -PARALLEL_MPY_FLAG}, \ + { "parallel-mpy", PARALLEL_MPY_FLAG}, \ + { "no-parallel-insns", -PARALLEL_PACK_FLAG}, \ + { "aliases", ALIASES_FLAG}, \ + { "no-aliases", -ALIASES_FLAG}, \ + { "", TARGET_DEFAULT} } + +/* Default target switches */ + +/* Play safe, not the fastest code. Note that setting PARALLEL_MPY +flag will set SMALL_REGISTER_CLASSES which can be a price to pay, +especially when MPY||ADD instructions are only generated very +infrequenctly. */ +#define TARGET_DEFAULT ALIASES_FLAG | RPTB_FLAG | PARALLEL_PACK_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 1 + +#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) + +/* -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) + +/* -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},\ + {"cpu=", &c4x_cpu_version_string} } + +/* Sometimes certain combinations of command options do not make sense + on a particular target machine. You can define a macro + `OVERRIDE_OPTIONS' to take account of this. This macro, if + defined, is executed once just after all the command options have + been parsed. */ + +extern void c4x_override_options (); +#define OVERRIDE_OPTIONS c4x_override_options () + + +/* 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 */ + +/* 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_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_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))) + +/* 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; \ + } \ + } \ + } + +/* 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' */ + SP_REG, /* 'b' */ + BK_REG, /* 'k' */ + 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", \ + "SP_REG", \ + "BK_REG", \ + "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. */ + + +#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 */ \ + 0x00100000, /* 'b' SP */ \ + 0x00080000, /* 'k' BK */ \ + 0x0e1eff00, /* 'c' AR0-AR7, IR0-IR1, RC, RS, RE, BK, SP */ \ + 0xfe1effff, /* 'r' R0-R11, AR0-AR7, IR0-IR1, RC, RS, RE, BK, SP */\ + 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 compiler allows +registers explicitly used in the rtl to be used as spill registers but +prevents the compiler from extending the lifetime of these registers. +Problems can occur if reload has to spill a register used explicitly +in the RTL if it has a long lifetime. This is only likely to be a problem +with a function having many variables and thus lots of spilling. + +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 + +/* + 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 + x - index register (ir0-ir1) + y - status register (st) + z - dp reg (dp) + + 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) == '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(VAL, C) \ + ( ((C) == 'G') ? (fp_zero_operand (VAL)) \ + : ((C) == 'H') ? (c4x_H_constant (VAL)) \ + : 0 ) + +#define EXTRA_CONSTRAINT(VAL, C) \ + ( ((C) == 'Q') ? (c4x_Q_constraint (VAL)) \ + : ((C) == 'R') ? (c4x_R_constraint (VAL)) \ + : ((C) == 'S') ? (c4x_S_constraint (VAL)) \ + : ((C) == 'T') ? (c4x_T_constraint (VAL)) \ + : 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 +#define HAVE_PRE_INCREMENT +#define HAVE_POST_DECREMENT +#define HAVE_PRE_DECREMENT +#define HAVE_PRE_MODIFY_REG +#define HAVE_POST_MODIFY_REG +#define HAVE_PRE_MODIFY_DISP +#define HAVE_POST_MODIFY_DISP + +/* What about LABEL_REF? */ +#define CONSTANT_ADDRESS_P(X) (GET_CODE (X) == SYMBOL_REF) + +#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 and reject + LABEL_REF, SYMBOL_REF, CONST, and HIGH codes. */ + +#define LEGITIMATE_CONSTANT_P(X) \ + (GET_CODE (X) == CONST_DOUBLE && c4x_H_constant (X) \ + || GET_CODE (X) == CONST_INT && c4x_I_constant (X)) + + +#define LEGITIMATE_DISPLACEMENT_P(X) IS_DISP8_CONST (INTVAL (X)) + +/* 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 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. */ + +#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 && 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) + +/* Make the Rx register a little easier to use so they are used for + calculations and the ARx registers are used for addressing. */ + +#define REGISTER_MOVE_COST(FROM, TO) \ +(EXPENSIVE_CLASS_P(TO) ? 5 : EXPENSIVE_CLASS_P(FROM) ? 4 : 3) + +/* 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 ON_EXIT(FUNC,ARG) atexit (FUNC) + +#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, "%20f", 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, "%20f", 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 MACHINE_DEPENDENT_COMBINE(INSNS) c4x_combine_parallel(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 + +/* Repeat block stuff (hook into strength_reduce() in loop.c). */ + +extern void c4x_rptb_process (); +#define REPEAT_BLOCK_PROCESS(START, END) c4x_rptb_process(START, END) + +/* 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}}, \ + {"call_operand", {REG, SYMBOL_REF}}, \ + {"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}}, \ + {"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 reg_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 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 st_reg_operand (); + +extern int call_operand (); + +extern int par_ind_operand (); + +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 void c4x_emit_libcall (); + +extern void c4x_emit_libcall3 (); + +extern void c4x_emit_libcall_mulhi (); + +extern int c4x_group1_reg_operand (); + +extern int c4x_group1_mem_operand (); + +extern int c4x_arx_reg_operand (); + +extern int legitimize_operands (); + +extern int valid_operands (); + +extern int valid_parallel_operands_4 (); + +extern int valid_parallel_operands_5 (); + +extern int valid_parallel_operands_6 (); diff --git a/gcc/config/c4x/c4x.md b/gcc/config/c4x/c4x.md new file mode 100644 index 0000000..21e62ea --- /dev/null +++ b/gcc/config/c4x/c4x.md @@ -0,0 +1,6798 @@ +;; Machine description for the TMS320C[34]x for GNU C compiler +;; Copyright (C) 1994, 1995, 1996, 1997 Free Software Foundation, Inc. + +;; Contributed by Michael Hayes (m.hayes@elec.canterbury.cri.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 : +; Set up addressing macros to handle direct memory references properly +; 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 [rfmHI] +; 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) +; f fp reg R0-R11 (sets CC when dst) +; g general reg, memory, constant +; h fp reg R0-R11 (sets CC when dst) +; i immediate int constant +; m memory +; 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 (value not explicit) +; t R0-R1 +; u R2-R3 +; 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 symbol ref (direct) +; < 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 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,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"))) + +(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,unarycc,binarycc,compare,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 + + +; +; 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))) + +; +; 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")]) + + +; Used when moving a constant label reference to an external +; location, this will make sure the original label is still +; used so the optimizer will not optimize it away. +; +(define_insn "set_ldp_use" + [(parallel [(set (match_operand:QI 0 "dp_reg_operand" "=z") + (high:QI (match_operand:QI 1 "" ""))) + (use (match_operand 2 "" ""))])] + "!TARGET_SMALL" + "* return (TARGET_C3X) ? \"ldp\\t%A1\" : \"ldpk\\t%A1\";" + [(set_attr "type" "ldp")]) + +(define_insn "set_high_use" + [(parallel [(set (match_operand:QI 0 "std_reg_operand" "=c") + (high:QI (match_operand:QI 1 "" ""))) + (use (match_operand 2 "" ""))])] + "!TARGET_C3X && !TARGET_SMALL" + "ldhi\\t^%H1,%0" + [(set_attr "type" "unary")]) + +(define_insn "set_ior_lo_use" + [(parallel [(set (match_operand:QI 0 "std_reg_operand" "=c") + (ior:QI (match_dup 0) + (and:QI (match_operand:QI 1 "" "") + (const_int 65535)))) + (use (match_operand 2 "" ""))])] + "!TARGET_C3X && !TARGET_SMALL" + "or\\t#%H1,%0" + [(set_attr "type" "unary")]) + +; +; 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" "rmI,rmI,r,O"))] + "reg_operand (operands[0], QImode) + || reg_operand (operands[1], QImode)" + "* + if (which_alternative == 2) + return \"sti\\t%1,%0\"; + + if (!TARGET_C3X && which_alternative == 3) + { + operands[1] = gen_rtx (CONST_INT, VOIDmode, + (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" "g")) + (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" "g") + (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" "g") +; (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 "src_operand" "") + (match_operand:QI 1 "src_operand" ""))] + "" + " + /* We shouldn't have to do this, since reload is supposed to + be able to do this if we have a memory constraint. */ + if (CONSTANT_P (operands[1]) + && !const_operand (operands[1], QImode)) + { + operands[1] = force_const_mem (QImode, operands[1]); + if (!memory_address_p (QImode, XEXP (operands[1], 0)) + && !reload_in_progress) + operands[1] = change_address (operands[1], QImode, + XEXP (operands[1], 0)); + } + + if (!reload_in_progress + && !reg_operand (operands[0], QImode) + && !reg_operand (operands[1], QImode) + && !(stik_const_operand (operands[1], QImode) + && !push_operand (operands[0], QImode))) + operands[1] = force_reg (QImode, operands[1]);") + +(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_operands_4 (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" "g,g"))) + (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" "g")) + (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" "g")) + (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" "g,g"))) + (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" "g")) + (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" "g")) + (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" "g"))) + (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" "g,g"))) + (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" "g")) + (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" "g")) + (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; + + /* 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. */ + + for (i = 0; i < INTVAL (operands[2]); i++) + emit_insn (gen_rotl_1_clobber (operands[0], operands[1])); + 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; + + /* 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. */ + + for (i = 0; i < INTVAL (operands[2]); i++) + emit_insn (gen_rotr_1_clobber (operands[0], operands[1])); + 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 +; +(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);") + +(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" "%rR,rS<>,0,rR,rS<>,0") + (match_operand:QI 2 "src_operand" "JR,rS<>,g,JR,rS<>,g"))) + (clobber (reg:CC_NOOV 21))] + "valid_operands (PLUS, operands, QImode)" + "@ + addi3\\t%2,%1,%0 + addi3\\t%2,%1,%0 + addi\\t%2,%0 + addi3\\t%2,%1,%0 + addi3\\t%2,%1,%0 + addi\\t%2,%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" "%rR,rS<>,0") + (match_operand:QI 2 "src_operand" "JR,rS<>,g")) + (const_int 0))) + (clobber (match_scratch:QI 0 "=d,?d,d"))] + "valid_operands (PLUS, operands, QImode)" + "@ + addi3\\t%2,%1,%0 + addi3\\t%2,%1,%0 + addi\\t%2,%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" "%rR,rS<>,0") + (neg: QI (match_operand:QI 2 "src_operand" "JR,rS<>,g")))) + (clobber (match_scratch:QI 0 "=d,?d,d"))] + "valid_operands (PLUS, operands, QImode)" + "@ + addi3\\t%2,%1,%0 + addi3\\t%2,%1,%0 + addi\\t%2,%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" "%rR,rS<>,0") + (match_operand:QI 2 "src_operand" "JR,rS<>,g"))) + (clobber (reg:CC_NOOV 21))]) + (set (reg:CC_NOOV 21) + (compare:CC_NOOV (match_dup 0) (const_int 0)))] + "valid_operands (PLUS, operands, QImode)" + "@ + addi3\\t%2,%1,%0 + addi3\\t%2,%1,%0 + addi\\t%2,%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" "%rR,rS<>,0") + (match_operand:QI 2 "src_operand" "JR,rS<>,g")) + (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)" + "@ + addi3\\t%2,%1,%0 + addi3\\t%2,%1,%0 + addi\\t%2,%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. +(define_insn "addqi3_noclobber" + [(set (match_operand:QI 0 "std_reg_operand" "=c,?c,c") + (plus:QI (match_operand:QI 1 "src_operand" "%rR,rS<>,0") + (match_operand:QI 2 "src_operand" "JR,rS<>,g")))] + "valid_operands (PLUS, operands, QImode)" + "@ + addi3\\t%2,%1,%0 + addi3\\t%2,%1,%0 + addi\\t%2,%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" "%rR,rS<>,0,rR,rS<>,0") + (match_operand:QI 2 "src_operand" "JR,rS<>,g,JR,rS<>,g"))) + (use (reg:CC_NOOV 21)) + (clobber (reg:CC_NOOV 21))] + "valid_operands (PLUS, operands, QImode)" + "@ + addc3\\t%2,%1,%0 + addc3\\t%2,%1,%0 + addc\\t%2,%0 + addc3\\t%2,%1,%0 + addc3\\t%2,%1,%0 + addc\\t%2,%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" "rR,rS<>,0,g,rR,rS<>,0,g") + (match_operand:QI 2 "src_operand" "JR,rS<>,g,0,JR,rS<>,g,0"))) + (clobber (reg:CC_NOOV 21))] + "valid_operands (MINUS, operands, QImode)" + "@ + 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 + subi\\t%2,%0 + subri\\t%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" "rR,rS<>,0,g") + (match_operand:QI 2 "src_operand" "JR,rS<>,g,0")) + (const_int 0))) + (clobber (match_scratch:QI 0 "=d,?d,d,d"))] + "valid_operands (MINUS, operands, QImode)" + "@ + subi3\\t%2,%1,%0 + subi3\\t%2,%1,%0 + subi\\t%2,%0 + subri\\t%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" "rR,rS<>,0,g") + (match_operand:QI 2 "src_operand" "JR,rS<>,g,0"))) + (clobber (reg:CC_NOOV 21))]) + (set (reg:CC_NOOV 21) + (compare:CC_NOOV (match_dup 0) (const_int 0)))] + "valid_operands (MINUS, operands, QImode)" + "@ + subi3\\t%2,%1,%0 + subi3\\t%2,%1,%0 + subi\\t%2,%0 + subri\\t%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" "rR,rS<>,0,g") + (match_operand:QI 2 "src_operand" "JR,rS<>,g,0")) + (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)" + "@ + subi3\\t%2,%1,%0 + subi3\\t%2,%1,%0 + subi\\t%2,%0 + subri\\t%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" "rR,rS<>,0,g,rR,rS<>,0,g") + (match_operand:QI 2 "src_operand" "JR,rS<>,0,g,JR,rS<>,g,0"))) + (use (reg:CC_NOOV 21)) + (clobber (reg:CC_NOOV 21))] + "valid_operands (MINUS, operands, QImode)" + "@ + 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 + subb\\t%2,%0 + subrb\\t%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" "rR,rS<>,0,g") + (match_operand:QI 2 "src_operand" "JR,rS<>,g,0")) + (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)" + "@ + subb3\\t%2,%1,%0 + subb3\\t%2,%1,%0 + subb\\t%2,%0 + subrb\\t%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" "%rR,rS<>,0,rR,rS<>,0") + (match_operand:QI 2 "src_operand" "JR,rS<>,g,JR,rS<>,g"))) + (clobber (reg:CC_NOOV 21))] + "valid_operands (MULT, operands, QImode)" + "* + if (which_alternative == 2 || which_alternative == 5) + { + 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" "%rR,rS<>,0") + (match_operand:QI 2 "src_operand" "JR,rS<>,g")) + (const_int 0))) + (clobber (match_scratch:QI 0 "=d,?d,d"))] + "valid_operands (MULT, operands, QImode)" + "* + if (which_alternative == 2) + { + 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" "%rR,rS<>,0") + (match_operand:QI 2 "src_operand" "JR,rS<>,g")) + (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 == 2) + { + 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" "%rR,rS<>,0,rR,rS<>,0") + (const_int 16777215))) + (sign_extend:QI + (and:QI (match_operand:QI 2 "src_operand" "JR,rS<>,g,JR,rS<>,g") + (const_int 16777215))))) + (clobber (reg:CC_NOOV 21))] + "TARGET_C3X && valid_operands (MULT, operands, QImode)" + "@ + mpyi3\\t%2,%1,%0 + mpyi3\\t%2,%1,%0 + mpyi\\t%2,%0 + mpyi3\\t%2,%1,%0 + mpyi3\\t%2,%1,%0 + mpyi\\t%2,%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" "%rR,rS<>,0,rR,rS<>,0")) + (sign_extend:HI (match_operand:QI 2 "src_operand" "JR,rS<>,g,JR,rS<>,g"))) + (const_int 32)))) + (clobber (reg:CC_NOOV 21))] + "!TARGET_C3X && valid_operands (MULT, operands, QImode)" + "@ + mpyshi3\\t%2,%1,%0 + mpyshi3\\t%2,%1,%0 + mpyshi\\t%2,%0 + mpyshi3\\t%2,%1,%0 + mpyshi3\\t%2,%1,%0 + mpyshi\\t%2,%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" "%rR,rS<>,0,rR,rS<>,0")) + (zero_extend:HI (match_operand:QI 2 "lsrc_operand" "JR,rS<>,g,JR,rS<>,g"))) + (const_int 32)))) + (clobber (reg:CC_NOOV 21))] + "!TARGET_C3X && valid_operands (MULT, operands, QImode)" + "@ + mpyuhi3\\t%2,%1,%0 + mpyuhi3\\t%2,%1,%0 + mpyuhi\\t%2,%0 + mpyuhi3\\t%2,%1,%0 + mpyuhi3\\t%2,%1,%0 + mpyuhi\\t%2,%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_clobber" + [(set (match_operand:QI 0 "reg_operand" "=d,?d,d,d,c,?c,c,c") + (and:QI (match_operand:QI 1 "src_operand" "%rR,rS<>,0,0,rR,rS<>,0,0") + (match_operand:QI 2 "tsrc_operand" "JR,rS<>,N,rLm,JR,rS<>,N,rLm"))) + (clobber (reg:CC 21))] + "valid_operands (AND, operands, QImode)" + "@ + 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 + andn\\t%N2,%0 + and\\t%2,%0" + [(set_attr "type" "binarycc,binarycc,binarycc,binarycc,binary,binary,binary,binary") + (set_attr "data" "int16,uint16,not_uint16,uint16,int16,uint16,not_uint16,uint16")]) + +(define_insn "*andqi3_test" + [(set (reg:CC 21) + (compare:CC (and:QI (match_operand:QI 1 "src_operand" "rR,rS<>,0,r") + (match_operand:QI 2 "tsrc_operand" "JR,rS<>,N,rLm")) + (const_int 0))) + (clobber (match_scratch:QI 0 "=X,X,d,X"))] + "valid_operands (AND, operands, QImode)" + "@ + tstb3\\t%2,%1 + tstb3\\t%2,%1 + andn\\t%N2,%0 + tstb\\t%2,%1" + [(set_attr "type" "binarycc,binarycc,binarycc,binarycc") + (set_attr "data" "int16,uint16,not_uint16,uint16")]) + +(define_peephole + [(parallel [(set (match_operand:QI 0 "ext_reg_operand" "=d,?d,d,d") + (and:QI (match_operand:QI 1 "src_operand" "%rR,rS<>,0,0") + (match_operand:QI 2 "tsrc_operand" "JR,rS<>,N,rLm"))) + (clobber (reg:CC 21))]) + (set (reg:CC 21) + (compare:CC (match_dup 0) (const_int 0)))] + "valid_operands (AND, operands, QImode)" + "@ + and3\\t%2,%1,%0 + and3\\t%2,%1,%0 + andn\\t%N2,%0 + and\\t%2,%0" + [(set_attr "type" "binarycc,binarycc,binarycc,binarycc") + (set_attr "data" "int16,uint16,not_uint16,uint16")]) + +(define_insn "*andqi3_set" + [(set (reg:CC 21) + (compare:CC (and:QI (match_operand:QI 1 "src_operand" "%rR,rS<>,0,0") + (match_operand:QI 2 "tsrc_operand" "JR,rS<>,N,rLm")) + (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)" + "@ + and3\\t%2,%1,%0 + and3\\t%2,%1,%0 + andn\\t%N2,%0 + and\\t%2,%0" + [(set_attr "type" "binarycc,binarycc,binarycc,binarycc") + (set_attr "data" "int16,uint16,not_uint16,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" "JR,rS<>,rLm,JR,rS<>,rLm")) + (match_operand:QI 1 "src_operand" "rR,rS<>,0,rR,rS<>,0"))) + (clobber (reg:CC 21))] + "valid_operands (AND, operands, QImode)" + "@ + andn3\\t%2,%1,%0 + andn3\\t%2,%1,%0 + andn\\t%2,%0 + andn3\\t%2,%1,%0 + andn3\\t%2,%1,%0 + andn\\t%2,%0" + [(set_attr "type" "binarycc,binarycc,binarycc,binary,binary,binary") + (set_attr "data" "int16,uint16,uint16,int16,uint16,uint16")]) + +(define_insn "*andnqi3_test" + [(set (reg:CC 21) + (compare:CC (and:QI (not:QI (match_operand:QI 2 "lsrc_operand" "JR,rS<>,rLm")) + (match_operand:QI 1 "src_operand" "rR,rS<>,0")) + (const_int 0))) + (clobber (match_scratch:QI 0 "=d,?d,d"))] + "valid_operands (AND, operands, QImode)" + "@ + andn3\\t%2,%1,%0 + andn3\\t%2,%1,%0 + andn\\t%2,%0" + [(set_attr "type" "binarycc,binarycc,binarycc") + (set_attr "data" "int16,uint16,uint16")]) + +(define_insn "*andnqi3_set" + [(set (reg:CC 21) + (compare:CC (and:QI (not:QI (match_operand:QI 2 "lsrc_operand" "JR,rS<>,rLm")) + (match_operand:QI 1 "src_operand" "rR,rS<>,0")) + (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)" + "@ + andn3\\t%2,%1,%0 + andn3\\t%2,%1,%0 + andn\\t%2,%0" + [(set_attr "type" "binarycc,binarycc,binarycc") + (set_attr "data" "int16,uint16,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" "%rR,rS<>,0,rR,rS<>,0") + (match_operand:QI 2 "lsrc_operand" "JR,rS<>,rLm,JR,rS<>,rLm"))) + (clobber (reg:CC 21))] + "valid_operands (IOR, operands, QImode)" + "@ + or3\\t%2,%1,%0 + or3\\t%2,%1,%0 + or\\t%2,%0 + or3\\t%2,%1,%0 + or3\\t%2,%1,%0 + or\\t%2,%0" + [(set_attr "type" "binarycc,binarycc,binarycc,binary,binary,binary") + (set_attr "data" "int16,uint16,uint16,int16,uint16,uint16")]) + +(define_insn "*iorqi3_test" + [(set (reg:CC 21) + (compare:CC (ior:QI (match_operand:QI 1 "src_operand" "%rR,rS<>,0") + (match_operand:QI 2 "lsrc_operand" "JR,rS<>,rLm")) + (const_int 0))) + (clobber (match_scratch:QI 0 "=d,?d,d"))] + "valid_operands (IOR, operands, QImode)" + "@ + or3\\t%2,%1,%0 + or3\\t%2,%1,%0 + or\\t%2,%0" + [(set_attr "type" "binarycc,binarycc,binarycc") + (set_attr "data" "int16,uint16,uint16")]) + +(define_peephole + [(parallel [(set (match_operand:QI 0 "ext_reg_operand" "=d,?d,d") + (ior:QI (match_operand:QI 1 "src_operand" "%rR,rS<>,0") + (match_operand:QI 2 "lsrc_operand" "JR,rS<>,rLm"))) + (clobber (reg:CC 21))]) + (set (reg:CC 21) + (compare:CC (match_dup 0) (const_int 0)))] + "valid_operands (IOR, operands, QImode)" + "@ + or3\\t%2,%1,%0 + or3\\t%2,%1,%0 + or\\t%2,%0" + [(set_attr "type" "binarycc,binarycc,binarycc") + (set_attr "data" "int16,uint16,uint16")]) + +(define_insn "*iorqi3_set" + [(set (reg:CC 21) + (compare:CC (ior:QI (match_operand:QI 1 "src_operand" "%rR,rS<>,0") + (match_operand:QI 2 "lsrc_operand" "JR,rS<>,rLm")) + (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)" + "@ + or3\\t%2,%1,%0 + or3\\t%2,%1,%0 + or\\t%2,%0" + [(set_attr "type" "binarycc,binarycc,binarycc") + (set_attr "data" "int16,uint16,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" "%rR,rS<>,0") + (match_operand:QI 2 "lsrc_operand" "JR,rS<>,rLm")))] + "valid_operands (IOR, operands, QImode)" + "@ + or3\\t%2,%1,%0 + or3\\t%2,%1,%0 + or\\t%2,%0" + [(set_attr "type" "binary,binary,binary") + (set_attr "data" "int16,uint16,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" "%rR,rS<>,0,rR,rS<>,0") + (match_operand:QI 2 "lsrc_operand" "JR,rS<>,rLm,JR,rS<>,rLm"))) + (clobber (reg:CC 21))] + "valid_operands (XOR, operands, QImode)" + "@ + xor3\\t%2,%1,%0 + xor3\\t%2,%1,%0 + xor\\t%2,%0 + xor3\\t%2,%1,%0 + xor3\\t%2,%1,%0 + xor\\t%2,%0" + [(set_attr "type" "binarycc,binarycc,binarycc,binary,binary,binary") + (set_attr "data" "int16,uint16,uint16,int16,uint16,uint16")]) + +(define_insn "*xorqi3_test" + [(set (reg:CC 21) + (compare:CC (xor:QI (match_operand:QI 1 "src_operand" "%rR,rS<>,0") + (match_operand:QI 2 "lsrc_operand" "JR,rS<>,rLm")) + (const_int 0))) + (clobber (match_scratch:QI 0 "=d,?d,d"))] + "valid_operands (XOR, operands, QImode)" + "@ + xor3\\t%2,%1,%0 + xor3\\t%2,%1,%0 + xor\\t%2,%0" + [(set_attr "type" "binarycc,binarycc,binarycc") + (set_attr "data" "int16,uint16,uint16")]) + +(define_insn "*xorqi3_set" + [(set (reg:CC 21) + (compare:CC (xor:QI (match_operand:QI 1 "src_operand" "%rR,rS<>,0") + (match_operand:QI 2 "lsrc_operand" "JR,rS<>,rLm")) + (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)" + "@ + xor3\\t%2,%1,%0 + xor3\\t%2,%1,%0 + xor\\t%2,%0" + [(set_attr "type" "binarycc,binarycc,binarycc") + (set_attr "data" "int16,uint16,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" "rR,rS<>,0,rR,rS<>,0") + (match_operand:QI 2 "src_operand" "JR,rS<>,g,JR,rS<>,g"))) + (clobber (reg:CC 21))] + "valid_operands (ASHIFT, operands, QImode)" + "@ + ash3\\t%2,%1,%0 + ash3\\t%2,%1,%0 + ash\\t%2,%0 + ash3\\t%2,%1,%0 + ash3\\t%2,%1,%0 + ash\\t%2,%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" "rR,rS<>,0,rR,rS<>,0") + (match_operand:QI 2 "src_operand" "JR,rS<>,g,JR,rS<>,g")) + (const_int 0))) + (set (match_operand:QI 0 "reg_operand" "=d,?d,d,c,?c,c") + (ashift:QI (match_dup 1) + (match_dup 2)))] + "valid_operands (ASHIFT, operands, QImode)" + "@ + ash3\\t%2,%1,%0 + ash3\\t%2,%1,%0 + ash\\t%2,%0 + ash3\\t%2,%1,%0 + ash3\\t%2,%1,%0 + ash\\t%2,%0" + [(set_attr "type" "binarycc,binarycc,binarycc,binary,binary,binary")]) +; 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" "rR,rS<>,0,rR,rS<>,0") + (unspec [(match_operand:QI 2 "src_operand" "JR,rS<>,g,JR,rS<>,g")] 3))) + (clobber (reg:CC 21))] + "valid_operands (ASHIFT, operands, QImode)" + "@ + lsh3\\t%2,%1,%0 + lsh3\\t%2,%1,%0 + lsh\\t%2,%0 + lsh3\\t%2,%1,%0 + lsh3\\t%2,%1,%0 + lsh\\t%2,%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);") + +; 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,0,r,r") + (match_operand:QI 2 "const_int_operand" "n,n,J,J")) + (const_int 0))) + (set (match_operand:QI 0 "reg_operand" "=?d,?c,d,c") + (lshiftrt:QI (match_dup 1) + (match_dup 2)))] + "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")]) + +(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" "rR,rS<>,0,rR,rS<>,0") + (neg:QI (match_operand:QI 2 "src_operand" "R,rS<>,rm,R,rS<>,rm")))) + (clobber (reg:CC 21))] + "valid_operands (LSHIFTRT, operands, QImode)" + "@ + lsh3\\t%2,%1,%0 + lsh3\\t%2,%1,%0 + lsh\\t%2,%0 + lsh3\\t%2,%1,%0 + lsh3\\t%2,%1,%0 + lsh\\t%2,%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,0,r,r") + (match_operand:QI 2 "const_int_operand" "n,n,J,J")) + (const_int 0))) + (set (match_operand:QI 0 "reg_operand" "=?d,?c,d,c") + (ashiftrt:QI (match_dup 1) + (match_dup 2)))] + "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")]) + +(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" "rR,rS<>,0,rR,rS<>,0") + (neg:QI (match_operand:QI 2 "src_operand" "R,rS<>,rm,R,rS<>,rm")))) + (clobber (reg:CC 21))] + "valid_operands (ASHIFTRT, operands, QImode)" + "@ + ash3\\t%2,%1,%0 + ash3\\t%2,%1,%0 + ash\\t%2,%0 + ash3\\t%2,%1,%0 + ash3\\t%2,%1,%0 + ash\\t%2,%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" "rR,?rS<>,r") + (match_operand:QI 1 "src_operand" "JR,rS<>,g")))] + "valid_operands (COMPARE, operands, QImode)" + "@ + cmpi3\\t%1,%0 + cmpi3\\t%1,%0 + cmpi\\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" "rR,?rS<>,r") + (match_operand:QI 1 "src_operand" "JR,rS<>,g")))] + "valid_operands (COMPARE, operands, QImode)" + "@ + cmpi3\\t%1,%0 + cmpi3\\t%1,%0 + cmpi\\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" "g,g") + (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_rtx (CONST_INT, VOIDmode, INTVAL (operands[3]) / 8); + return \"lb%3\\t%1,%0\"; + } + operands[3] = gen_rtx (CONST_INT, VOIDmode, 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" "g") + (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_rtx (CONST_INT, VOIDmode, INTVAL (operands[3]) / 8); + return \"lb%3\\t%1,%0\"; + } + operands[3] = gen_rtx (CONST_INT, VOIDmode, 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" "g") + (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_rtx (CONST_INT, VOIDmode, INTVAL (operands[3]) / 8); + return \"lb%3\\t%1,%0\"; + } + operands[3] = gen_rtx (CONST_INT, VOIDmode, 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" "g,g") + (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_rtx (CONST_INT, VOIDmode, INTVAL (operands[3]) / 8); + return \"lbu%3\\t%1,%0\"; + } + operands[3] = gen_rtx (CONST_INT, VOIDmode, 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" "g") + (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_rtx (CONST_INT, VOIDmode, INTVAL (operands[3]) / 8); + return \"lbu%3\\t%1,%0\"; + } + operands[3] = gen_rtx (CONST_INT, VOIDmode, 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" "g") + (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") + (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) + { + operands[3] = gen_rtx (CONST_INT, VOIDmode, INTVAL (operands[3]) / 8); + return \"lbu%3\\t%1,%0\"; + } + operands[3] = gen_rtx (CONST_INT, VOIDmode, 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" "g,g")) + (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) + { + operands[2] = gen_rtx (CONST_INT, VOIDmode, INTVAL (operands[2]) / 8); + return \"mb%2\\t%3,%0\"; + } + else if (INTVAL (operands[1]) == 16) + { + operands[2] = gen_rtx (CONST_INT, VOIDmode, INTVAL (operands[2]) / 16); + return \"mh%2\\t%3,%0\"; + } + 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 "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" "g")) + (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_rtx (CONST_INT, VOIDmode, INTVAL (operands[2]) / 8); + return \"mb%2\\t%3,%0\"; + } + operands[2] = gen_rtx (CONST_INT, VOIDmode, 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 (CONSTANT_P (operands[1]) && !const_operand (operands[1], QFmode)) + { + operands[1] = force_const_mem (QFmode, operands[1]); + if (!memory_address_p (QFmode, XEXP (operands[1], 0)) + && !reload_in_progress) + operands[1] = change_address (operands[1], QFmode, + XEXP (operands[1], 0)); + } + + if (!reload_in_progress + && !reg_operand (operands[0], QFmode) + && !reg_operand (operands[1], QFmode)) + operands[1] = force_reg (QFmode, operands[1]); + ") + +; 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" "fmH,f"))] + "reg_operand (operands[0], QFmode) + || reg_operand (operands[1], QFmode)" + "@ + 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" "fmH")) +; (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" "fmH") + (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" "fmH") + (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_operands_4 (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" "fmH"))) + (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" "fmH")) + (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" "fmH")) + (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" "fmH"))) + (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" "fmH")) + (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" "fmH")) + (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" "g"))) + (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" "g")) + (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 2) + (if_then_else:QF (lt (reg:CC 21) (const_int 0)) + (mem:QF (symbol_ref:QF "*___unsfltconst")) + (match_dup 2))) + (parallel [(set (match_operand:QF 0 "reg_operand" "") + (plus:QF (match_dup 2) (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); + ") + +(define_insn "floatqihf2" + [(set (match_operand:HF 0 "reg_operand" "=h") + (float:HF (match_operand:QI 1 "src_operand" "g"))) + (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" "fmH,fmH"))) + (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" "fmH")) + (const_int 0))) + (set (match_operand:QI 0 "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;") + +(define_expand "fixuns_truncqfqi2" + [(set (match_dup 2) (match_dup 4)) + (set (reg:CC 21) + (compare:CC (match_operand:QF 1 "reg_operand" "") + (mem:QF (symbol_ref "*___unsfltcompare")))) + (set (match_dup 2) + (if_then_else:QF (ge (reg:CC 21) (const_int 0)) + (mem:QF (symbol_ref "*___unsfltconst")) + (match_dup 2))) + (parallel [(set (match_dup 3) + (minus:QF (match_dup 1) (match_dup 2))) + (clobber (reg:CC_NOOV 21))]) + (parallel [(set (match_operand:QI 0 "reg_operand" "") + (fix:QI (match_dup 3))) + (clobber (reg:CC 21))])] + "" + "operands[2] = gen_reg_rtx (QFmode); + operands[3] = gen_reg_rtx (QFmode); + operands[4] = CONST0_RTX (QFmode); + ") + +(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" "fmH")] 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" "fmH")] 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" "fmH")] 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" "%fR,fS<>,0") + (match_operand:QF 2 "src_operand" "R,fS<>,fmH"))) + (clobber (reg:CC_NOOV 21))] + "valid_operands (PLUS, operands, QFmode)" + "@ + addf3\\t%2,%1,%0 + addf3\\t%2,%1,%0 + addf\\t%2,%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" "%fR,fS<>,0") + (match_operand:QF 2 "src_operand" "R,fS<>,fmH")) + (match_operand:QF 3 "fp_zero_operand" "G,G,G"))) + (clobber (match_scratch:QF 0 "=f,?f,f"))] + "valid_operands (PLUS, operands, QFmode)" + "@ + addf3\\t%2,%1,%0 + addf3\\t%2,%1,%0 + addf\\t%2,%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" "%fR,fS<>,0") + (match_operand:QF 2 "src_operand" "R,fS<>,fmH")) + (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)" + "@ + addf3\\t%2,%1,%0 + addf3\\t%2,%1,%0 + addf\\t%2,%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" "fR,fS<>,0,fmH") + (match_operand:QF 2 "src_operand" "R,fS<>,fmH,0"))) + (clobber (reg:CC_NOOV 21))] + "valid_operands (MINUS, operands, QFmode)" + "@ + subf3\\t%2,%1,%0 + subf3\\t%2,%1,%0 + subf\\t%2,%0 + subrf\\t%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" "fR,fS<>,0,fmH") + (match_operand:QF 2 "src_operand" "R,fS<>,fmH,0")) + (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)" + "@ + subf3\\t%2,%1,%0 + subf3\\t%2,%1,%0 + subf\\t%2,%0 + subrf\\t%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" "fR,fS<>,0,fmH") + (match_operand:QF 2 "src_operand" "R,fS<>,fmH,0")) + (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)" + "@ + subf3\\t%2,%1,%0 + subf3\\t%2,%1,%0 + subf\\t%2,%0 + subrf\\t%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" "%fR,fS<>,0") + (match_operand:QF 2 "src_operand" "R,fS<>,fmH"))) + (clobber (reg:CC_NOOV 21))] + "valid_operands (MULT, operands, QFmode)" + "@ + mpyf3\\t%2,%1,%0 + mpyf3\\t%2,%1,%0 + mpyf\\t%2,%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" "%fR,fS<>,0") + (match_operand:QF 2 "src_operand" "R,fS<>,fmH")) + (match_operand:QF 3 "fp_zero_operand" "G,G,G"))) + (clobber (match_scratch:QF 0 "=f,?f,f"))] + "valid_operands (MULT, operands, QFmode)" + "@ + mpyf3\\t%2,%1,%0 + mpyf3\\t%2,%1,%0 + mpyf\\t%2,%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" "%fR,fS<>,0") + (match_operand:QF 2 "src_operand" "R,fS<>,fmH")) + (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)" + "@ + mpyf3\\t%2,%1,%0 + mpyf3\\t%2,%1,%0 + mpyf\\t%2,%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" "fR,?fS<>,f") + (match_operand:QF 1 "src_operand" "R,fS<>,fmH")))] + "valid_operands (COMPARE, operands, QFmode)" + "@ + cmpf3\\t%1,%0 + cmpf3\\t%1,%0 + cmpf\\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" "fR,?fS<>,f") + (match_operand:QF 1 "src_operand" "R,fS<>,fmH")))] + "valid_operands (COMPARE, operands, QFmode)" + "@ + cmpf3\\t%1,%0 + cmpf3\\t%1,%0 + cmpf\\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" "g,0") + (match_operand:QI 3 "src_operand" "0,g")))] + "" + "@ + 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" "g,0") + (match_operand:QI 3 "src_operand" "0,g")))] + "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" "fmH,0") + (match_operand:QF 3 "src_operand" "0,fmH")))] + "" + "@ + 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" "fmH,0") + (match_operand:QF 3 "src_operand" "0,fmH")))] + "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" + "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 "*floatqiqf_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" + "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")]) + +; +; 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" + "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" + "subf3\\t%2,%1,%0\\n||\\tstf\\t%4,%3" + [(set_attr "type" "binarycc")]) + +; +; PARALLEL INTEGER INSTRUCTIONS +; +; These patterns are under development + +; +; 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" + "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" + "ash3\\t%2,%1,%0\\n||\\tsti\\t%4,%3" + [(set_attr "type" "binarycc")]) + +; +; ASH(right)/STI +; + +(define_insn "*ashlqi3_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" + "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" + "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" + "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")]) + +; +; 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" + "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" + "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" + "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 (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 (1, 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 (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 (1, 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 (match_operand:QI 0 "call_operand" "T,!o") + (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\\t%C0 + callu\\t%R0" + [(set_attr "type" "call,call")]) + +; LAJ requires R11 (31) for the return address +(define_insn "*laj" + [(call (match_operand:QI 0 "call_operand" "T,!o") + (match_operand:QI 1 "general_operand" "")) + (clobber (reg:QI 31))] + ;; Operand 1 not really used on the C4x. + + "!TARGET_C3X" + "* + if (which_alternative == 0) + { + if (final_sequence) + return \"laj\\t%C0\"; + else + return \"call\\t%C0\"; + } + if (which_alternative == 1) + { + if (final_sequence) + return \"laju\\t%R0\"; + else + return \"callu\\t%R0\"; + }" + [(set_attr "type" "laj,laj")]) + +(define_expand "call" + [(parallel [(call (match_operand:QI 0 "call_operand" "") + (match_operand:QI 1 "general_operand" "")) + (clobber (reg:QI 31))])] + "" + "") + +(define_insn "*callv_c3x" + [(set (match_operand 0 "" "=r,r") + (call (match_operand:QI 1 "call_operand" "T,!o") + (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\\t%C1 + callu\\t%R1" + [(set_attr "type" "call,call")]) + +; LAJ requires R11 (31) for the return address +(define_insn "*lajv" + [(set (match_operand 0 "" "=r,r") + (call (match_operand:QI 1 "call_operand" "T,!o") + (match_operand:QI 2 "general_operand" ""))) + (clobber (reg:QI 31))] + ;; Operand 0 and 2 not really used in the C30 instruction. + + "!TARGET_C3X" + "* + if (which_alternative == 0) + { + if (final_sequence) + return \"laj\\t%C1\"; + else + return \"call\\t%C1\"; + } + if (which_alternative == 1) + { + if (final_sequence) + return \"laju\\t%R1\"; + else + return \"callu\\t%R1\"; + }" + [(set_attr "type" "laj,laj")]) + +(define_expand "call_value" + [(parallel [(set (match_operand 0 "" "") + (call (match_operand:QI 1 "call_operand" "") + (match_operand:QI 2 "general_operand" ""))) + (clobber (reg:QI 31))])] + "" + "") + +(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,!m") + (const_int 0)) + (label_ref (match_operand 1 "" "")) + (pc))) + (set (match_dup 0) + (plus:QI (match_dup 0) + (const_int -1)))] + "TARGET_DB && TARGET_LOOP_UNSIGNED" + "* + if (IS_ADDR_REG (REGNO (operands[0]))) + { + return \"dbu%#\\t%0,%l1\"; + } + else if (IS_EXT_REG (REGNO (operands[0]))) + { + return \"subi\\t1,%0\\n\\tbge%#\\t%l1\"; + } + else + { + return \"push\\tr0\\n\\tldi\\t%0,r0\\n\\tsubi\\t1,r0\\n\\tsti\\tr0,%0\\n\\tpop\\tr0\\n\\tbhs%#\\t%l1\"; + } + " + [(set_attr "type" "db")]) + +(define_insn "decrement_and_branch_until_zero" + [(set (pc) + (if_then_else (ge (plus:QI (match_operand:QI 0 "addr_reg_operand" "+a,!d,!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)))] + "TARGET_DB && find_reg_note (insn, REG_NONNEG, 0)" + "* + if (IS_ADDR_REG (REGNO (operands[0]))) + { + return \"dbu%#\\t%0,%l1\"; + } + else if (IS_EXT_REG (REGNO (operands[0]))) + { + return \"subi\\t1,%0\\n\\tbge%#\\t%l1\"; + } + else + { + return \"push\\tr0\\n\\tldi\\t%0,r0\\n\\tsubi\\t1,r0\\n\\tsti\\tr0,%0\\n\\t\\tpop\\tr0\\n\\tbhs%#\\t%l1\"; + } + " + [(set_attr "type" "db")]) + +; +; MISC INSTRUCTIONS +; + +; +; NOP +; +(define_insn "nop" + [(const_int 0)] + "" + "nop") +; Default to misc type attr. + +; +; RPTS +; +; Should we disallow RPTS if we get a silly number of shifts? +(define_insn "rpts" + [(set (reg:QI 27) + (unspec [(match_operand:QI 0 "src_operand" "g")] 2)) + (clobber (reg:QI 25)) + (clobber (reg:QI 26))] + "" + "rpts\\t%0" + [(set_attr "type" "repeat")]) + +; +; RPTB +; +(define_insn "rptb_top" + [(set (reg:QI 25) (label_ref (match_operand 0 "" ""))) + (set (reg:QI 26) (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")]) + +; operand 0 is the loop depth +; operand 1 is the loop count +(define_expand "repeat_block_top" + [(set (reg:QI 27) (match_operand:QI 1 "src_operand" "")) + (use (match_operand:QI 0 "immediate_operand" "")) + (parallel[(set (reg:QI 25) (label_ref (match_operand 2 "" ""))) + (set (reg:QI 26) (label_ref (match_operand 3 "" "")))])] + "" + "if (CONSTANT_P (operands[1]) + && !const_operand (operands[1], QImode)) + operands[1] = force_const_mem (QImode, operands[1]);" + ) + +; operand 0 is the loop depth +(define_insn "repeat_block_end" + [(set (pc) + (if_then_else (ne (reg:QI 27) (const_int 0)) + (label_ref (match_operand 1 "" "")) + (pc))) + (use (match_operand:QI 0 "immediate_operand" "")) + (use (reg:QI 25)) + (use (reg:QI 26)) + (set (reg:QI 27) + (plus:QI (reg:QI 27) + (const_int -1)))] + "" + "* + return c4x_rptb_nop_p(insn) ? \"nop\" : \"\";" + [(set_attr "type" "repeat")]) + +; to prevent labels being coalesced and to leave a space to sink insns +; out of a repeat block loop. +(define_insn "repeat_block_filler" + [(unspec [(const_int 0)] 7)] + "" + "" + [(set_attr "type" "repeat")]) + + +(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" "repeat")]) + +; 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 "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); + }") + +(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 (CONSTANT_P (operands[1])) + { + operands[1] = force_const_mem (HFmode, operands[1]); + if (!memory_address_p (HFmode, XEXP (operands[1], 0)) + && !reload_in_progress) + operands[1] = change_address (operands[1], HFmode, + XEXP (operands[1], 0)); + } + + /* Memory to memory copies must go through a register. */ + if (GET_CODE (operands[1]) == MEM && GET_CODE (operands[0]) == MEM + && !reload_in_progress) + operands[1] = force_reg (HFmode, operands[1]); +") + +(define_insn "*movhf_noclobber_reg" + [(set (match_operand:HF 0 "reg_operand" "=h") + (match_operand:HF 1 "reg_operand" "h"))] + "" + "ldfu\\t%1,%0" + [(set_attr "type" "unary")]) + +; The predicates could be tightened to disallow constants +(define_insn "*movhf_noclobber" + [(set (match_operand:HF 0 "src_operand" "=h,m") + (match_operand:HF 1 "src_operand" "m,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" "fmH")))] + "" + "@ + 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" "g")] 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" "%h,0") + (match_operand:HF 2 "reg_or_const_operand" "h,H"))) + (clobber (reg:CC_NOOV 21))] + "" + "@ + addf3\\t%2,%1,%0 + addf\\t%2,%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" "h,0,H") + (match_operand:HF 2 "reg_or_const_operand" "h,H,0"))) + (clobber (reg:CC_NOOV 21))] + "" + "@ + subf3\\t%2,%1,%0 + subf\\t%2,%0 + subrf\\t%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" "%h,0") + (match_operand:HF 2 "reg_or_const_operand" "h,hH"))) + (clobber (reg:CC_NOOV 21))] + "" + "@ + mpyf3\\t%2,%1,%0 + mpyf\\t%2,%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 +; + +; 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 (CONSTANT_P (operands[1])) + { + /* We don't need to force all constants into memory. + This could be improved.... */ + operands[1] = force_const_mem (HImode, operands[1]); + if (!memory_address_p (HImode, XEXP (operands[1], 0)) + && !reload_in_progress) + operands[1] = change_address (operands[1], HImode, + XEXP (operands[1], 0)); + } + + /* Memory to memory copies must go through a register. */ + if (GET_CODE (operands[1]) == MEM && GET_CODE (operands[0]) == MEM + && !reload_in_progress) + operands[1] = force_reg (HImode, operands[1]); +") + +; 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" "drm,r"))] + "reg_operand (operands[0], HImode) + || reg_operand (operands[1], HImode)" + "#" + [(set_attr "type" "multi,multi")]) + +(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))" + [(set (match_dup 2) (match_dup 3)) + (set (match_dup 4) (match_dup 5))] + "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 "extendqihi2" + [(set (match_operand:HI 0 "reg_operand" "=dc") + (sign_extend:HI (match_operand:QI 1 "src_operand" "g"))) + (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" "g"))) + (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" "g"))) + (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" "g"))) + (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" "g"))) + (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" "%rR,rS<>,0") + (match_operand:HI 2 "src_operand" "R,rS<>,rm"))) + (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" "rR,rS<>,0") + (match_operand:HI 2 "src_operand" "R,rS<>,rm"))) + (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" "%rR,rS<>,0") + (match_operand:HI 2 "src_operand" "R,rS<>,rm"))) + (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" "%rR,rS<>,0") + (match_operand:HI 2 "src_operand" "R,rS<>,rm"))) + (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 (AND, operands, HImode);") + + +(define_insn "*xorhi3_clobber" + [(set (match_operand:HI 0 "reg_operand" "=d,?d,d") + (xor:HI (match_operand:HI 1 "src_operand" "%rR,rS<>,0") + (match_operand:HI 2 "src_operand" "R,rS<>,rm"))) + (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);") + +; This should do all the dirty work with define_split +(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_rtx (CONST_INT, VOIDmode, + (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; + } + 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 "src_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_rtx (CONST_INT, VOIDmode, + (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; + } + 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 "src_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_rtx (CONST_INT, VOIDmode, + (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_rtx (CONST_INT, + VOIDmode, 31))); + DONE; + } + 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 "src_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)))])] + "!c4x_label_conflict (insn, operands[2], operands[1])" + "db%I3\\t%0,%l1\\n\\tb%3\\t%l2") + +(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)))])] + "!c4x_label_conflict (insn, operands[2], operands[1])" + "db%I3\\t%0,%l1\\n\\tb%3\\t%l2") + +; +; Peepholes to convert 'call label; rets' into jump label +; +(define_peephole + [(parallel [(call (match_operand:QI 0 "call_operand" "T,!o") + (match_operand:QI 1 "general_operand" "")) + (clobber (reg:QI 31))]) + (return)] + "c4x_null_epilogue_p ()" + "@ + br%#\\t%C0 + bu%#\\t%R0" + [(set_attr "type" "jump,jump")]) + +(define_peephole + [(parallel [(set (match_operand 0 "" "") + (call (match_operand:QI 1 "call_operand" "T,!o") + (match_operand:QI 2 "general_operand" ""))) + (clobber (reg:QI 31))]) + (return)] + "c4x_null_epilogue_p ()" + "@ + br%#\\t%C1 + bu%#\\t%R1" + [(set_attr "type" "jump,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") + + diff --git a/gcc/config/c4x/t-c4x b/gcc/config/c4x/t-c4x new file mode 100644 index 0000000..e696474 --- /dev/null +++ b/gcc/config/c4x/t-c4x @@ -0,0 +1,29 @@ +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 + +# We do not have DF or DI types (or SF and SI for that matter), +# so fake out the libgcc2 compilation. +LIBGCC2_CFLAGS = -O2 -Dexit=unused_exit $(GCC_CFLAGS) $(LIBGCC2_INCLUDES) -DDF=HF -DDI=HI -DSF=QF -DSI=QI + +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 = + +# Don't make objective C because we can't compile the libraries. +LANGUAGES = c proto c++ + +# C[34]x has its own float and limits.h +TARGET_FLOAT_H=config/c4x/c4x-float.h +TARGET_LIMITS_H=config/c4x/c4x-limits.h diff --git a/gcc/config/c4x/xm-c4x.h b/gcc/config/c4x/xm-c4x.h new file mode 100644 index 0000000..dc329eb --- /dev/null +++ b/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 + diff --git a/gcc/ginclude/va-c4x.h b/gcc/ginclude/va-c4x.h new file mode 100644 index 0000000..c73c6d5 --- /dev/null +++ b/gcc/ginclude/va-c4x.h @@ -0,0 +1,34 @@ +/* GNU C varargs support for the TMS320C[34]x */ + +/* C[34]x arguments grow in weird ways (downwards) that the standard + varargs stuff can't handle. */ + +#ifndef __GNUC_VA_LIST +#define __GNUC_VA_LIST + +typedef void *__gnuc_va_list; + +#endif /* not __GNUC_VA_LIST */ + +/* If this is for internal libc use, don't define anything but + __gnuc_va_list. */ +#if defined (_STDARG_H) || defined (_VARARGS_H) + +#ifdef _STDARG_H /* stdarg.h support */ + +#define va_start(AP,LASTARG) AP=(__gnuc_va_list) __builtin_next_arg (LASTARG) + +#else /* varargs.h support */ + +#define __va_ellipsis ... +#define va_alist __builtin_va_alist +#define va_dcl int __builtin_va_alist; __va_ellipsis +#define va_start(AP) AP=(__gnuc_va_list) ((int *)&__builtin_va_alist + 1) + +#endif /* _STDARG_H */ + +#define va_end(AP) ((void) 0) +#define va_arg(AP,TYPE) (AP = (__gnuc_va_list) ((char *) (AP) - sizeof(TYPE)), \ + *((TYPE *) ((char *) (AP)))) + +#endif /* defined (_STDARG_H) || defined (_VARARGS_H) */ -- 2.7.4