OpenCores
URL https://opencores.org/ocsvn/openrisc/openrisc/trunk

Subversion Repositories openrisc

Compare Revisions

  • This comparison shows the changes necessary to convert path
    /openrisc/trunk/gnu-old/gcc-4.2.2/gcc/config/c4x
    from Rev 154 to Rev 816
    Reverse comparison

Rev 154 → Rev 816

/c4x-c.c
0,0 → 1,160
/* Subroutines for the C front end on the TMS320C[34]x
Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001,
2007 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 GCC.
 
GCC is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3, or (at your option)
any later version.
 
GCC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
 
You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING3. If not see
<http://www.gnu.org/licenses/>. */
 
#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "tm.h"
#include "tree.h"
#include "toplev.h"
#include "cpplib.h"
#include "c-pragma.h"
#include "tm_p.h"
 
static int c4x_parse_pragma (const char *, tree *, tree *);
 
/* 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
 
*/
 
/* Parse a C4x pragma, of the form ( function [, "section"] ) \n.
FUNC is loaded with the IDENTIFIER_NODE of the function, SECT with
the STRING_CST node of the string. If SECT is null, then this
pragma doesn't take a section string. Returns 0 for a good pragma,
-1 for a malformed pragma. */
#define BAD(gmsgid, arg) \
do { warning (OPT_Wpragmas, gmsgid, arg); return -1; } while (0)
 
static int
c4x_parse_pragma (name, func, sect)
const char *name;
tree *func;
tree *sect;
{
tree f, s, x;
 
if (pragma_lex (&x) != CPP_OPEN_PAREN)
BAD ("missing '(' after '#pragma %s' - ignored", name);
 
if (pragma_lex (&f) != CPP_NAME)
BAD ("missing function name in '#pragma %s' - ignored", name);
 
if (sect)
{
if (pragma_lex (&x) != CPP_COMMA)
BAD ("malformed '#pragma %s' - ignored", name);
if (pragma_lex (&s) != CPP_STRING)
BAD ("missing section name in '#pragma %s' - ignored", name);
*sect = s;
}
 
if (pragma_lex (&x) != CPP_CLOSE_PAREN)
BAD ("missing ')' for '#pragma %s' - ignored", name);
 
if (pragma_lex (&x) != CPP_EOF)
warning (OPT_Wpragmas, "junk at end of '#pragma %s'", name);
 
*func = f;
return 0;
}
 
void
c4x_pr_CODE_SECTION (pfile)
cpp_reader *pfile ATTRIBUTE_UNUSED;
{
tree func, sect;
 
if (c4x_parse_pragma ("CODE_SECTION", &func, &sect))
return;
code_tree = chainon (code_tree,
build_tree_list (func,
build_tree_list (NULL_TREE, sect)));
}
 
void
c4x_pr_DATA_SECTION (pfile)
cpp_reader *pfile ATTRIBUTE_UNUSED;
{
tree func, sect;
 
if (c4x_parse_pragma ("DATA_SECTION", &func, &sect))
return;
data_tree = chainon (data_tree,
build_tree_list (func,
build_tree_list (NULL_TREE, sect)));
}
 
void
c4x_pr_FUNC_IS_PURE (pfile)
cpp_reader *pfile ATTRIBUTE_UNUSED;
{
tree func;
 
if (c4x_parse_pragma ("FUNC_IS_PURE", &func, 0))
return;
pure_tree = chainon (pure_tree, build_tree_list (func, NULL_TREE));
}
 
void
c4x_pr_FUNC_NEVER_RETURNS (pfile)
cpp_reader *pfile ATTRIBUTE_UNUSED;
{
tree func;
 
if (c4x_parse_pragma ("FUNC_NEVER_RETURNS", &func, 0))
return;
noreturn_tree = chainon (noreturn_tree, build_tree_list (func, NULL_TREE));
}
 
void
c4x_pr_INTERRUPT (pfile)
cpp_reader *pfile ATTRIBUTE_UNUSED;
{
tree func;
 
if (c4x_parse_pragma ("INTERRUPT", &func, 0))
return;
interrupt_tree = chainon (interrupt_tree, build_tree_list (func, NULL_TREE));
}
 
/* Used for FUNC_CANNOT_INLINE, FUNC_EXT_CALLED, FUNC_IS_SYSTEM,
FUNC_NO_GLOBAL_ASG, and FUNC_NO_IND_ASG. */
void
c4x_pr_ignored (pfile)
cpp_reader *pfile ATTRIBUTE_UNUSED;
{
}
/c4x.c
0,0 → 1,4623
/* Subroutines for assembler code output on the TMS320C[34]x
Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2003,
2004, 2005, 2007
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 GCC.
 
GCC is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3, or (at your option)
any later version.
 
GCC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
 
You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING3. If not see
<http://www.gnu.org/licenses/>. */
 
/* Some output-actions in c4x.md need these. */
#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "tm.h"
#include "rtl.h"
#include "tree.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 "conditions.h"
#include "output.h"
#include "function.h"
#include "expr.h"
#include "optabs.h"
#include "libfuncs.h"
#include "flags.h"
#include "recog.h"
#include "ggc.h"
#include "cpplib.h"
#include "toplev.h"
#include "tm_p.h"
#include "target.h"
#include "target-def.h"
#include "langhooks.h"
 
rtx smulhi3_libfunc;
rtx umulhi3_libfunc;
rtx fix_truncqfhi2_libfunc;
rtx fixuns_truncqfhi2_libfunc;
rtx fix_trunchfhi2_libfunc;
rtx fixuns_trunchfhi2_libfunc;
rtx floathiqf2_libfunc;
rtx floatunshiqf2_libfunc;
rtx floathihf2_libfunc;
rtx floatunshihf2_libfunc;
 
static int c4x_leaf_function;
 
static const char *const float_reg_names[] = FLOAT_REGISTER_NAMES;
 
/* Array of the smallest class containing reg number REGNO, indexed by
REGNO. Used by REGNO_REG_CLASS in c4x.h. We assume that all these
registers are available and set the class to NO_REGS for registers
that the target switches say are unavailable. */
 
enum reg_class c4x_regclass_map[FIRST_PSEUDO_REGISTER] =
{
/* Reg Modes Saved. */
R0R1_REGS, /* R0 QI, QF, HF No. */
R0R1_REGS, /* R1 QI, QF, HF No. */
R2R3_REGS, /* R2 QI, QF, HF No. */
R2R3_REGS, /* R3 QI, QF, HF No. */
EXT_LOW_REGS, /* R4 QI, QF, HF QI. */
EXT_LOW_REGS, /* R5 QI, QF, HF QI. */
EXT_LOW_REGS, /* R6 QI, QF, HF QF. */
EXT_LOW_REGS, /* R7 QI, QF, HF QF. */
ADDR_REGS, /* AR0 QI No. */
ADDR_REGS, /* AR1 QI No. */
ADDR_REGS, /* AR2 QI No. */
ADDR_REGS, /* AR3 QI QI. */
ADDR_REGS, /* AR4 QI QI. */
ADDR_REGS, /* AR5 QI QI. */
ADDR_REGS, /* AR6 QI QI. */
ADDR_REGS, /* AR7 QI QI. */
DP_REG, /* DP QI No. */
INDEX_REGS, /* IR0 QI No. */
INDEX_REGS, /* IR1 QI No. */
BK_REG, /* BK QI QI. */
SP_REG, /* SP QI No. */
ST_REG, /* ST CC No. */
NO_REGS, /* DIE/IE No. */
NO_REGS, /* IIE/IF No. */
NO_REGS, /* IIF/IOF No. */
INT_REGS, /* RS QI No. */
INT_REGS, /* RE QI No. */
RC_REG, /* RC QI No. */
EXT_REGS, /* R8 QI, QF, HF QI. */
EXT_REGS, /* R9 QI, QF, HF No. */
EXT_REGS, /* R10 QI, QF, HF No. */
EXT_REGS, /* R11 QI, QF, HF No. */
};
 
enum machine_mode c4x_caller_save_map[FIRST_PSEUDO_REGISTER] =
{
/* Reg Modes Saved. */
HFmode, /* R0 QI, QF, HF No. */
HFmode, /* R1 QI, QF, HF No. */
HFmode, /* R2 QI, QF, HF No. */
HFmode, /* R3 QI, QF, HF No. */
QFmode, /* R4 QI, QF, HF QI. */
QFmode, /* R5 QI, QF, HF QI. */
QImode, /* R6 QI, QF, HF QF. */
QImode, /* R7 QI, QF, HF QF. */
QImode, /* AR0 QI No. */
QImode, /* AR1 QI No. */
QImode, /* AR2 QI No. */
QImode, /* AR3 QI QI. */
QImode, /* AR4 QI QI. */
QImode, /* AR5 QI QI. */
QImode, /* AR6 QI QI. */
QImode, /* AR7 QI QI. */
VOIDmode, /* DP QI No. */
QImode, /* IR0 QI No. */
QImode, /* IR1 QI No. */
QImode, /* BK QI QI. */
VOIDmode, /* SP QI No. */
VOIDmode, /* ST CC No. */
VOIDmode, /* DIE/IE No. */
VOIDmode, /* IIE/IF No. */
VOIDmode, /* IIF/IOF No. */
QImode, /* RS QI No. */
QImode, /* RE QI No. */
VOIDmode, /* RC QI No. */
QFmode, /* R8 QI, QF, HF QI. */
HFmode, /* R9 QI, QF, HF No. */
HFmode, /* R10 QI, QF, HF No. */
HFmode, /* R11 QI, QF, HF No. */
};
 
 
/* Test and compare insns in c4x.md store the information needed to
generate branch and scc insns here. */
 
rtx c4x_compare_op0;
rtx c4x_compare_op1;
 
int c4x_cpu_version = 40; /* CPU version C30/31/32/33/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;
tree naked_tree = NULL_TREE;
 
/* Forward declarations */
static bool c4x_handle_option (size_t, const char *, int);
static int c4x_isr_reg_used_p (unsigned int);
static int c4x_leaf_function_p (void);
static int c4x_naked_function_p (void);
static int c4x_immed_int_constant (rtx);
static int c4x_immed_float_constant (rtx);
static int c4x_R_indirect (rtx);
static void c4x_S_address_parse (rtx , int *, int *, int *, int *);
static int c4x_valid_operands (enum rtx_code, rtx *, enum machine_mode, int);
static int c4x_arn_reg_operand (rtx, enum machine_mode, unsigned int);
static int c4x_arn_mem_operand (rtx, enum machine_mode, unsigned int);
static void c4x_file_start (void);
static void c4x_file_end (void);
static void c4x_check_attribute (const char *, tree, tree, tree *);
static int c4x_r11_set_p (rtx);
static int c4x_rptb_valid_p (rtx, rtx);
static void c4x_reorg (void);
static int c4x_label_ref_used_p (rtx, rtx);
static tree c4x_handle_fntype_attribute (tree *, tree, tree, int, bool *);
const struct attribute_spec c4x_attribute_table[];
static void c4x_insert_attributes (tree, tree *);
static void c4x_asm_named_section (const char *, unsigned int, tree);
static int c4x_adjust_cost (rtx, rtx, rtx, int);
static void c4x_globalize_label (FILE *, const char *);
static bool c4x_rtx_costs (rtx, int, int, int *);
static int c4x_address_cost (rtx);
static void c4x_init_libfuncs (void);
static void c4x_external_libcall (rtx);
static rtx c4x_struct_value_rtx (tree, int);
static tree c4x_gimplify_va_arg_expr (tree, tree, tree *, tree *);
/* Initialize the GCC target structure. */
#undef TARGET_ASM_BYTE_OP
#define TARGET_ASM_BYTE_OP "\t.word\t"
#undef TARGET_ASM_ALIGNED_HI_OP
#define TARGET_ASM_ALIGNED_HI_OP NULL
#undef TARGET_ASM_ALIGNED_SI_OP
#define TARGET_ASM_ALIGNED_SI_OP NULL
#undef TARGET_ASM_FILE_START
#define TARGET_ASM_FILE_START c4x_file_start
#undef TARGET_ASM_FILE_START_FILE_DIRECTIVE
#define TARGET_ASM_FILE_START_FILE_DIRECTIVE true
#undef TARGET_ASM_FILE_END
#define TARGET_ASM_FILE_END c4x_file_end
 
#undef TARGET_ASM_EXTERNAL_LIBCALL
#define TARGET_ASM_EXTERNAL_LIBCALL c4x_external_libcall
 
/* Play safe, not the fastest code. */
#undef TARGET_DEFAULT_TARGET_FLAGS
#define TARGET_DEFAULT_TARGET_FLAGS (MASK_ALIASES | MASK_PARALLEL \
| MASK_PARALLEL_MPY | MASK_RPTB)
#undef TARGET_HANDLE_OPTION
#define TARGET_HANDLE_OPTION c4x_handle_option
 
#undef TARGET_ATTRIBUTE_TABLE
#define TARGET_ATTRIBUTE_TABLE c4x_attribute_table
 
#undef TARGET_INSERT_ATTRIBUTES
#define TARGET_INSERT_ATTRIBUTES c4x_insert_attributes
 
#undef TARGET_INIT_BUILTINS
#define TARGET_INIT_BUILTINS c4x_init_builtins
 
#undef TARGET_EXPAND_BUILTIN
#define TARGET_EXPAND_BUILTIN c4x_expand_builtin
 
#undef TARGET_SCHED_ADJUST_COST
#define TARGET_SCHED_ADJUST_COST c4x_adjust_cost
 
#undef TARGET_ASM_GLOBALIZE_LABEL
#define TARGET_ASM_GLOBALIZE_LABEL c4x_globalize_label
 
#undef TARGET_RTX_COSTS
#define TARGET_RTX_COSTS c4x_rtx_costs
#undef TARGET_ADDRESS_COST
#define TARGET_ADDRESS_COST c4x_address_cost
 
#undef TARGET_MACHINE_DEPENDENT_REORG
#define TARGET_MACHINE_DEPENDENT_REORG c4x_reorg
 
#undef TARGET_INIT_LIBFUNCS
#define TARGET_INIT_LIBFUNCS c4x_init_libfuncs
 
#undef TARGET_STRUCT_VALUE_RTX
#define TARGET_STRUCT_VALUE_RTX c4x_struct_value_rtx
 
#undef TARGET_GIMPLIFY_VA_ARG_EXPR
#define TARGET_GIMPLIFY_VA_ARG_EXPR c4x_gimplify_va_arg_expr
 
struct gcc_target targetm = TARGET_INITIALIZER;
/* Implement TARGET_HANDLE_OPTION. */
 
static bool
c4x_handle_option (size_t code, const char *arg, int value)
{
switch (code)
{
case OPT_m30: c4x_cpu_version = 30; return true;
case OPT_m31: c4x_cpu_version = 31; return true;
case OPT_m32: c4x_cpu_version = 32; return true;
case OPT_m33: c4x_cpu_version = 33; return true;
case OPT_m40: c4x_cpu_version = 40; return true;
case OPT_m44: c4x_cpu_version = 44; return true;
 
case OPT_mcpu_:
if (arg[0] == 'c' || arg[0] == 'C')
arg++;
value = atoi (arg);
switch (value)
{
case 30: case 31: case 32: case 33: case 40: case 44:
c4x_cpu_version = value;
return true;
}
return false;
 
default:
return true;
}
}
 
/* 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 (void)
{
/* Convert foo / 8.0 into foo * 0.125, etc. */
set_fast_math_flags (1);
 
/* We should phase out the following at some stage.
This provides compatibility with the old -mno-aliases option. */
if (! TARGET_ALIASES && ! flag_argument_noalias)
flag_argument_noalias = 1;
 
if (!TARGET_C3X)
target_flags |= MASK_MPYI | MASK_DB;
 
if (optimize < 2)
target_flags &= ~(MASK_RPTB | MASK_PARALLEL);
 
if (!TARGET_PARALLEL)
target_flags &= ~MASK_PARALLEL_MPY;
}
 
 
/* This is called before c4x_override_options. */
 
void
c4x_optimization_options (int level ATTRIBUTE_UNUSED,
int size ATTRIBUTE_UNUSED)
{
/* Scheduling before register allocation can screw up global
register allocation, especially for functions that use MPY||ADD
instructions. The benefit we gain we get by scheduling before
register allocation is probably marginal anyhow. */
flag_schedule_insns = 0;
}
 
 
/* Write an ASCII string. */
 
#define C4X_ASCII_LIMIT 40
 
void
c4x_output_ascii (FILE *stream, const char *ptr, int len)
{
char sbuf[C4X_ASCII_LIMIT + 1];
int s, l, special, first = 1, onlys;
 
if (len)
fprintf (stream, "\t.byte\t");
 
for (s = l = 0; len > 0; --len, ++ptr)
{
onlys = 0;
 
/* Escape " and \ with a \". */
special = *ptr == '\"' || *ptr == '\\';
 
/* If printable - add to buff. */
if ((! TARGET_TI || ! special) && *ptr >= 0x20 && *ptr < 0x7f)
{
if (special)
sbuf[s++] = '\\';
sbuf[s++] = *ptr;
if (s < C4X_ASCII_LIMIT - 1)
continue;
onlys = 1;
}
if (s)
{
if (first)
first = 0;
else
{
fputc (',', stream);
l++;
}
 
sbuf[s] = 0;
fprintf (stream, "\"%s\"", sbuf);
l += s + 2;
if (TARGET_TI && l >= 80 && len > 1)
{
fprintf (stream, "\n\t.byte\t");
first = 1;
l = 0;
}
s = 0;
}
if (onlys)
continue;
 
if (first)
first = 0;
else
{
fputc (',', stream);
l++;
}
 
fprintf (stream, "%d", *ptr);
l += 3;
if (TARGET_TI && l >= 80 && len > 1)
{
fprintf (stream, "\n\t.byte\t");
first = 1;
l = 0;
}
}
if (s)
{
if (! first)
fputc (',', stream);
 
sbuf[s] = 0;
fprintf (stream, "\"%s\"", sbuf);
s = 0;
}
fputc ('\n', stream);
}
 
 
int
c4x_hard_regno_mode_ok (unsigned 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_REGNO (regno);
 
case QFmode: /* Float, Double (32 bits). */
case HFmode: /* Long Double (40 bits). */
return IS_EXT_REGNO (regno);
 
case CCmode: /* Condition Codes. */
case CC_NOOVmode: /* Condition Codes. */
return IS_ST_REGNO (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_REGNO (regno)
&& IS_INT_REGNO (regno + 1)
&& (regno & 1) == 0;
 
default:
return 0; /* We don't support these modes. */
}
 
return 0;
}
 
/* Return nonzero if REGNO1 can be renamed to REGNO2. */
int
c4x_hard_regno_rename_ok (unsigned int regno1, unsigned int regno2)
{
/* We cannot copy call saved registers from mode QI into QF or from
mode QF into QI. */
if (IS_FLOAT_CALL_SAVED_REGNO (regno1) && IS_INT_CALL_SAVED_REGNO (regno2))
return 0;
if (IS_INT_CALL_SAVED_REGNO (regno1) && IS_FLOAT_CALL_SAVED_REGNO (regno2))
return 0;
/* We cannot copy from an extended (40 bit) register to a standard
(32 bit) register because we only set the condition codes for
extended registers. */
if (IS_EXT_REGNO (regno1) && ! IS_EXT_REGNO (regno2))
return 0;
if (IS_EXT_REGNO (regno2) && ! IS_EXT_REGNO (regno1))
return 0;
return 1;
}
 
/* 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 const 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 const 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 (CUMULATIVE_ARGS *cum, tree fntype, rtx libname)
{
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 (! targetm.calls.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 (CUMULATIVE_ARGS *cum, enum machine_mode mode,
tree type, int 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
&& ! targetm.calls.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 (CUMULATIVE_ARGS *cum, enum machine_mode mode,
tree type, int named)
{
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;
}
 
/* This marks the last argument. We don't need to pass this through
to the call insn. */
if (type == void_type_node)
return 0;
 
if (! TARGET_MEMPARM
&& named
&& type
&& ! targetm.calls.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;
}
 
/* C[34]x arguments grow in weird ways (downwards) that the standard
varargs stuff can't handle.. */
 
static tree
c4x_gimplify_va_arg_expr (tree valist, tree type,
tree *pre_p ATTRIBUTE_UNUSED,
tree *post_p ATTRIBUTE_UNUSED)
{
tree t;
bool indirect;
 
indirect = pass_by_reference (NULL, TYPE_MODE (type), type, false);
if (indirect)
type = build_pointer_type (type);
 
t = build2 (PREDECREMENT_EXPR, TREE_TYPE (valist), valist,
build_int_cst (NULL_TREE, int_size_in_bytes (type)));
t = fold_convert (build_pointer_type (type), t);
t = build_va_arg_indirect_ref (t);
 
if (indirect)
t = build_va_arg_indirect_ref (t);
 
return t;
}
 
 
static int
c4x_isr_reg_used_p (unsigned int regno)
{
/* Don't save/restore FP or ST, we handle them separately. */
if (regno == FRAME_POINTER_REGNUM
|| IS_ST_REGNO (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_REGNO (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_REGNO (regno)
|| ((regs_ever_live[regno] || call_used_regs[regno])
&& fixed_regs[regno] == 0);
}
 
 
static int
c4x_leaf_function_p (void)
{
/* 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_naked_function_p (void)
{
tree type;
 
type = TREE_TYPE (current_function_decl);
return lookup_attribute ("naked", TYPE_ATTRIBUTES (type)) != NULL;
}
 
 
int
c4x_interrupt_function_p (void)
{
const char *cfun_name;
if (lookup_attribute ("interrupt",
TYPE_ATTRIBUTES (TREE_TYPE (current_function_decl))))
return 1;
 
/* Look for TI style c_intnn. */
cfun_name = current_function_name ();
return cfun_name[0] == 'c'
&& cfun_name[1] == '_'
&& cfun_name[2] == 'i'
&& cfun_name[3] == 'n'
&& cfun_name[4] == 't'
&& ISDIGIT (cfun_name[5])
&& ISDIGIT (cfun_name[6]);
}
 
void
c4x_expand_prologue (void)
{
unsigned int regno;
int size = get_frame_size ();
rtx insn;
 
/* 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 needlessly push the frame pointer. */
int dont_push_ar3;
 
/* For __naked__ function don't build a prologue. */
if (c4x_naked_function_p ())
{
return;
}
/* For __interrupt__ function build specific prologue. */
if (c4x_interrupt_function_p ())
{
c4x_leaf_function = c4x_leaf_function_p ();
insn = emit_insn (gen_push_st ());
RTX_FRAME_RELATED_P (insn) = 1;
if (size)
{
insn = emit_insn (gen_pushqi ( gen_rtx_REG (QImode, AR3_REGNO)));
RTX_FRAME_RELATED_P (insn) = 1;
insn = emit_insn (gen_movqi (gen_rtx_REG (QImode, AR3_REGNO),
gen_rtx_REG (QImode, SP_REGNO)));
RTX_FRAME_RELATED_P (insn) = 1;
/* We require that an ISR uses fewer than 32768 words of
local variables, otherwise we have to go to lots of
effort to save a register, load it with the desired size,
adjust the stack pointer, and then restore the modified
register. Frankly, I think it is a poor ISR that
requires more than 32767 words of local temporary
storage! */
if (size > 32767)
error ("ISR %s requires %d words of local vars, max is 32767",
current_function_name (), size);
 
insn = emit_insn (gen_addqi3 (gen_rtx_REG (QImode, SP_REGNO),
gen_rtx_REG (QImode, SP_REGNO),
GEN_INT (size)));
RTX_FRAME_RELATED_P (insn) = 1;
}
for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
{
if (c4x_isr_reg_used_p (regno))
{
if (regno == DP_REGNO)
{
insn = emit_insn (gen_push_dp ());
RTX_FRAME_RELATED_P (insn) = 1;
}
else
{
insn = emit_insn (gen_pushqi (gen_rtx_REG (QImode, regno)));
RTX_FRAME_RELATED_P (insn) = 1;
if (IS_EXT_REGNO (regno))
{
insn = emit_insn (gen_pushqf
(gen_rtx_REG (QFmode, regno)));
RTX_FRAME_RELATED_P (insn) = 1;
}
}
}
}
/* 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])
{
insn = emit_insn (gen_andn_st (GEN_INT(~0x100)));
RTX_FRAME_RELATED_P (insn) = 1;
}
 
/* Reload DP reg if we are paranoid about some turkey
violating small memory model rules. */
if (TARGET_SMALL && TARGET_PARANOID)
{
insn = emit_insn (gen_set_ldp_prologue
(gen_rtx_REG (QImode, DP_REGNO),
gen_rtx_SYMBOL_REF (QImode, "data_sec")));
RTX_FRAME_RELATED_P (insn) = 1;
}
}
else
{
if (frame_pointer_needed)
{
if ((size != 0)
|| (current_function_args_size != 0)
|| (optimize < 2))
{
insn = emit_insn (gen_pushqi ( gen_rtx_REG (QImode, AR3_REGNO)));
RTX_FRAME_RELATED_P (insn) = 1;
insn = emit_insn (gen_movqi (gen_rtx_REG (QImode, AR3_REGNO),
gen_rtx_REG (QImode, SP_REGNO)));
RTX_FRAME_RELATED_P (insn) = 1;
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)
{
insn = emit_insn (gen_movqi (gen_rtx_REG (QImode, R1_REGNO),
GEN_INT(size >> 16)));
RTX_FRAME_RELATED_P (insn) = 1;
insn = emit_insn (gen_lshrqi3 (gen_rtx_REG (QImode, R1_REGNO),
gen_rtx_REG (QImode, R1_REGNO),
GEN_INT(-16)));
RTX_FRAME_RELATED_P (insn) = 1;
}
else
{
insn = emit_insn (gen_movqi (gen_rtx_REG (QImode, R1_REGNO),
GEN_INT(size & ~0xffff)));
RTX_FRAME_RELATED_P (insn) = 1;
}
insn = emit_insn (gen_iorqi3 (gen_rtx_REG (QImode, R1_REGNO),
gen_rtx_REG (QImode, R1_REGNO),
GEN_INT(size & 0xffff)));
RTX_FRAME_RELATED_P (insn) = 1;
insn = emit_insn (gen_addqi3 (gen_rtx_REG (QImode, SP_REGNO),
gen_rtx_REG (QImode, SP_REGNO),
gen_rtx_REG (QImode, R1_REGNO)));
RTX_FRAME_RELATED_P (insn) = 1;
}
else if (size != 0)
{
/* Local vars take up less than 32767 words, so we can directly
add the number. */
insn = emit_insn (gen_addqi3 (gen_rtx_REG (QImode, SP_REGNO),
gen_rtx_REG (QImode, SP_REGNO),
GEN_INT (size)));
RTX_FRAME_RELATED_P (insn) = 1;
}
for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
{
if (regs_ever_live[regno] && ! call_used_regs[regno])
{
if (IS_FLOAT_CALL_SAVED_REGNO (regno))
{
if (TARGET_PRESERVE_FLOAT)
{
insn = emit_insn (gen_pushqi
(gen_rtx_REG (QImode, regno)));
RTX_FRAME_RELATED_P (insn) = 1;
}
insn = emit_insn (gen_pushqf (gen_rtx_REG (QFmode, regno)));
RTX_FRAME_RELATED_P (insn) = 1;
}
else if ((! dont_push_ar3) || (regno != AR3_REGNO))
{
insn = emit_insn (gen_pushqi ( gen_rtx_REG (QImode, regno)));
RTX_FRAME_RELATED_P (insn) = 1;
}
}
}
}
}
 
 
void
c4x_expand_epilogue(void)
{
int regno;
int jump = 0;
int dont_pop_ar3;
rtx insn;
int size = get_frame_size ();
/* For __naked__ function build no epilogue. */
if (c4x_naked_function_p ())
{
insn = emit_jump_insn (gen_return_from_epilogue ());
RTX_FRAME_RELATED_P (insn) = 1;
return;
}
 
/* 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 (regno == DP_REGNO)
{
insn = emit_insn (gen_pop_dp ());
RTX_FRAME_RELATED_P (insn) = 1;
}
else
{
/* We have to use unspec because the compiler will delete insns
that are not call-saved. */
if (IS_EXT_REGNO (regno))
{
insn = emit_insn (gen_popqf_unspec
(gen_rtx_REG (QFmode, regno)));
RTX_FRAME_RELATED_P (insn) = 1;
}
insn = emit_insn (gen_popqi_unspec (gen_rtx_REG (QImode, regno)));
RTX_FRAME_RELATED_P (insn) = 1;
}
}
if (size)
{
insn = emit_insn (gen_subqi3 (gen_rtx_REG (QImode, SP_REGNO),
gen_rtx_REG (QImode, SP_REGNO),
GEN_INT(size)));
RTX_FRAME_RELATED_P (insn) = 1;
insn = emit_insn (gen_popqi
(gen_rtx_REG (QImode, AR3_REGNO)));
RTX_FRAME_RELATED_P (insn) = 1;
}
insn = emit_insn (gen_pop_st ());
RTX_FRAME_RELATED_P (insn) = 1;
insn = emit_jump_insn (gen_return_from_interrupt_epilogue ());
RTX_FRAME_RELATED_P (insn) = 1;
}
else
{
if (frame_pointer_needed)
{
if ((size != 0)
|| (current_function_args_size != 0)
|| (optimize < 2))
{
insn = emit_insn
(gen_movqi (gen_rtx_REG (QImode, R2_REGNO),
gen_rtx_MEM (QImode,
gen_rtx_PLUS
(QImode, gen_rtx_REG (QImode,
AR3_REGNO),
constm1_rtx))));
RTX_FRAME_RELATED_P (insn) = 1;
/* We already have the return value and the fp,
so we need to add those to the stack. */
size += 2;
jump = 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 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;
}
}
/* 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 (IS_FLOAT_CALL_SAVED_REGNO (regno))
{
insn = emit_insn (gen_popqf_unspec
(gen_rtx_REG (QFmode, regno)));
RTX_FRAME_RELATED_P (insn) = 1;
if (TARGET_PRESERVE_FLOAT)
{
insn = emit_insn (gen_popqi_unspec
(gen_rtx_REG (QImode, regno)));
RTX_FRAME_RELATED_P (insn) = 1;
}
}
else
{
insn = emit_insn (gen_popqi (gen_rtx_REG (QImode, regno)));
RTX_FRAME_RELATED_P (insn) = 1;
}
}
}
if (frame_pointer_needed)
{
if ((size != 0)
|| (current_function_args_size != 0)
|| (optimize < 2))
{
/* Restore the old FP. */
insn = emit_insn
(gen_movqi
(gen_rtx_REG (QImode, AR3_REGNO),
gen_rtx_MEM (QImode, gen_rtx_REG (QImode, AR3_REGNO))));
RTX_FRAME_RELATED_P (insn) = 1;
}
}
if (size > 32767)
{
/* Local vars are too big, it will take multiple operations
to decrement SP. */
if (TARGET_C3X)
{
insn = emit_insn (gen_movqi (gen_rtx_REG (QImode, R3_REGNO),
GEN_INT(size >> 16)));
RTX_FRAME_RELATED_P (insn) = 1;
insn = emit_insn (gen_lshrqi3 (gen_rtx_REG (QImode, R3_REGNO),
gen_rtx_REG (QImode, R3_REGNO),
GEN_INT(-16)));
RTX_FRAME_RELATED_P (insn) = 1;
}
else
{
insn = emit_insn (gen_movqi (gen_rtx_REG (QImode, R3_REGNO),
GEN_INT(size & ~0xffff)));
RTX_FRAME_RELATED_P (insn) = 1;
}
insn = emit_insn (gen_iorqi3 (gen_rtx_REG (QImode, R3_REGNO),
gen_rtx_REG (QImode, R3_REGNO),
GEN_INT(size & 0xffff)));
RTX_FRAME_RELATED_P (insn) = 1;
insn = emit_insn (gen_subqi3 (gen_rtx_REG (QImode, SP_REGNO),
gen_rtx_REG (QImode, SP_REGNO),
gen_rtx_REG (QImode, R3_REGNO)));
RTX_FRAME_RELATED_P (insn) = 1;
}
else if (size != 0)
{
/* Local vars take up less than 32768 words, so we can directly
subtract the number. */
insn = emit_insn (gen_subqi3 (gen_rtx_REG (QImode, SP_REGNO),
gen_rtx_REG (QImode, SP_REGNO),
GEN_INT(size)));
RTX_FRAME_RELATED_P (insn) = 1;
}
if (jump)
{
insn = emit_jump_insn (gen_return_indirect_internal
(gen_rtx_REG (QImode, R2_REGNO)));
RTX_FRAME_RELATED_P (insn) = 1;
}
else
{
insn = emit_jump_insn (gen_return_from_epilogue ());
RTX_FRAME_RELATED_P (insn) = 1;
}
}
}
 
 
int
c4x_null_epilogue_p (void)
{
int regno;
 
if (reload_completed
&& ! c4x_naked_function_p ()
&& ! c4x_interrupt_function_p ()
&& ! current_function_calls_alloca
&& ! current_function_args_size
&& ! (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 1;
return 0;
}
return 1;
}
 
 
int
c4x_emit_move_sequence (rtx *operands, enum machine_mode mode)
{
rtx op0 = operands[0];
rtx op1 = operands[1];
 
if (! reload_in_progress
&& ! REG_P (op0)
&& ! REG_P (op1)
&& ! (stik_const_operand (op1, mode) && ! push_operand (op0, mode)))
op1 = force_reg (mode, op1);
 
if (GET_CODE (op1) == LO_SUM
&& GET_MODE (op1) == Pmode
&& dp_reg_operand (XEXP (op1, 0), mode))
{
/* expand_increment will sometimes create a LO_SUM immediate
address. Undo this silliness. */
op1 = XEXP (op1, 1);
}
if (symbolic_address_operand (op1, mode))
{
if (TARGET_LOAD_ADDRESS)
{
/* Alias analysis seems to do a better job if we force
constant addresses to memory after reload. */
emit_insn (gen_load_immed_address (op0, op1));
return 1;
}
else
{
/* Stick symbol or label address into the constant pool. */
op1 = force_const_mem (Pmode, op1);
}
}
else if (mode == HFmode && CONSTANT_P (op1) && ! LEGITIMATE_CONSTANT_P (op1))
{
/* We could be a lot smarter about loading some of these
constants... */
op1 = force_const_mem (mode, op1);
}
 
/* Convert (MEM (SYMREF)) to a (MEM (LO_SUM (REG) (SYMREF)))
and emit associated (HIGH (SYMREF)) if large memory model.
c4x_legitimize_address could be used to do this,
perhaps by calling validize_address. */
if (TARGET_EXPOSE_LDP
&& ! (reload_in_progress || reload_completed)
&& GET_CODE (op1) == MEM
&& symbolic_address_operand (XEXP (op1, 0), Pmode))
{
rtx dp_reg = gen_rtx_REG (Pmode, DP_REGNO);
if (! TARGET_SMALL)
emit_insn (gen_set_ldp (dp_reg, XEXP (op1, 0)));
op1 = change_address (op1, mode,
gen_rtx_LO_SUM (Pmode, dp_reg, XEXP (op1, 0)));
}
 
if (TARGET_EXPOSE_LDP
&& ! (reload_in_progress || reload_completed)
&& GET_CODE (op0) == MEM
&& symbolic_address_operand (XEXP (op0, 0), Pmode))
{
rtx dp_reg = gen_rtx_REG (Pmode, DP_REGNO);
if (! TARGET_SMALL)
emit_insn (gen_set_ldp (dp_reg, XEXP (op0, 0)));
op0 = change_address (op0, mode,
gen_rtx_LO_SUM (Pmode, dp_reg, XEXP (op0, 0)));
}
 
if (GET_CODE (op0) == SUBREG
&& mixed_subreg_operand (op0, mode))
{
/* We should only generate these mixed mode patterns
during RTL generation. If we need do it later on
then we'll have to emit patterns that won't clobber CC. */
if (reload_in_progress || reload_completed)
abort ();
if (GET_MODE (SUBREG_REG (op0)) == QImode)
op0 = SUBREG_REG (op0);
else if (GET_MODE (SUBREG_REG (op0)) == HImode)
{
op0 = copy_rtx (op0);
PUT_MODE (op0, QImode);
}
else
abort ();
 
if (mode == QFmode)
emit_insn (gen_storeqf_int_clobber (op0, op1));
else
abort ();
return 1;
}
 
if (GET_CODE (op1) == SUBREG
&& mixed_subreg_operand (op1, mode))
{
/* We should only generate these mixed mode patterns
during RTL generation. If we need do it later on
then we'll have to emit patterns that won't clobber CC. */
if (reload_in_progress || reload_completed)
abort ();
if (GET_MODE (SUBREG_REG (op1)) == QImode)
op1 = SUBREG_REG (op1);
else if (GET_MODE (SUBREG_REG (op1)) == HImode)
{
op1 = copy_rtx (op1);
PUT_MODE (op1, QImode);
}
else
abort ();
 
if (mode == QFmode)
emit_insn (gen_loadqf_int_clobber (op0, op1));
else
abort ();
return 1;
}
 
if (mode == QImode
&& reg_operand (op0, mode)
&& const_int_operand (op1, mode)
&& ! IS_INT16_CONST (INTVAL (op1))
&& ! IS_HIGH_CONST (INTVAL (op1)))
{
emit_insn (gen_loadqi_big_constant (op0, op1));
return 1;
}
 
if (mode == HImode
&& reg_operand (op0, mode)
&& const_int_operand (op1, mode))
{
emit_insn (gen_loadhi_big_constant (op0, op1));
return 1;
}
 
/* Adjust operands in case we have modified them. */
operands[0] = op0;
operands[1] = op1;
 
/* Emit normal pattern. */
return 0;
}
 
 
void
c4x_emit_libcall (rtx libcall, enum rtx_code code,
enum machine_mode dmode, enum machine_mode smode,
int noperands, rtx *operands)
{
rtx ret;
rtx insns;
rtx equiv;
 
start_sequence ();
switch (noperands)
{
case 2:
ret = emit_library_call_value (libcall, NULL_RTX, 1, dmode, 1,
operands[1], smode);
equiv = gen_rtx_fmt_e (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_fmt_ee (code, dmode, operands[1], operands[2]);
break;
 
default:
abort ();
}
 
insns = get_insns ();
end_sequence ();
emit_libcall_block (insns, operands[0], ret, equiv);
}
 
 
void
c4x_emit_libcall3 (rtx libcall, enum rtx_code code,
enum machine_mode mode, rtx *operands)
{
c4x_emit_libcall (libcall, code, mode, mode, 3, operands);
}
 
 
void
c4x_emit_libcall_mulhi (rtx libcall, enum rtx_code code,
enum machine_mode mode, rtx *operands)
{
rtx ret;
rtx insns;
rtx equiv;
 
start_sequence ();
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_fmt_e (code, HImode, operands[1]),
gen_rtx_fmt_e (code, HImode, operands[2])),
GEN_INT (32)));
insns = get_insns ();
end_sequence ();
emit_libcall_block (insns, operands[0], ret, equiv);
}
 
 
int
c4x_legitimate_address_p (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 recognize an operand
being pushed on the stack. */
 
case PRE_DEC:
case PRE_INC:
case POST_DEC:
if (mode != QImode && mode != QFmode)
return 0;
 
case POST_INC:
base = XEXP (addr, 0);
if (! REG_P (base))
return 0;
break;
 
case PRE_MODIFY:
case POST_MODIFY:
{
rtx op0 = XEXP (addr, 0);
rtx op1 = XEXP (addr, 1);
 
if (mode != QImode && mode != QFmode)
return 0;
 
if (! REG_P (op0)
|| (GET_CODE (op1) != PLUS && GET_CODE (op1) != MINUS))
return 0;
base = XEXP (op1, 0);
if (! REG_P (base))
return 0;
if (REGNO (base) != REGNO (op0))
return 0;
if (REG_P (XEXP (op1, 1)))
indx = XEXP (op1, 1);
else
disp = XEXP (op1, 1);
}
break;
/* Register indirect. */
case REG:
base = addr;
break;
 
/* Register indirect with displacement or index. */
case PLUS:
{
rtx op0 = XEXP (addr, 0);
rtx op1 = XEXP (addr, 1);
enum rtx_code code0 = GET_CODE (op0);
 
switch (code0)
{
case REG:
if (REG_P (op1))
{
base = op0; /* Base + index. */
indx = op1;
if (IS_INDEX_REG (base) || IS_ADDR_REG (indx))
{
base = op1;
indx = op0;
}
}
else
{
base = op0; /* Base + displacement. */
disp = op1;
}
break;
 
default:
return 0;
}
}
break;
 
/* Direct addressing with DP register. */
case LO_SUM:
{
rtx op0 = XEXP (addr, 0);
rtx op1 = XEXP (addr, 1);
 
/* HImode and HFmode direct memory references aren't truly
offsettable (consider case at end of data page). We
probably get better code by loading a pointer and using an
indirect memory reference. */
if (mode == HImode || mode == HFmode)
return 0;
 
if (!REG_P (op0) || REGNO (op0) != DP_REGNO)
return 0;
 
if ((GET_CODE (op1) == SYMBOL_REF || GET_CODE (op1) == LABEL_REF))
return 1;
 
if (GET_CODE (op1) == CONST)
return 1;
return 0;
}
break;
 
/* Direct addressing with some work for the assembler... */
case CONST:
/* Direct addressing. */
case LABEL_REF:
case SYMBOL_REF:
if (! TARGET_EXPOSE_LDP && ! strict && mode != HFmode && mode != HImode)
return 1;
/* These need to be converted to a LO_SUM (...).
LEGITIMIZE_RELOAD_ADDRESS will do this during reload. */
return 0;
 
/* Do not allow direct memory access to absolute addresses.
This is more pain than it's 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_REG (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_REG (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 (rtx orig ATTRIBUTE_UNUSED,
enum machine_mode mode ATTRIBUTE_UNUSED)
{
if (GET_CODE (orig) == SYMBOL_REF
|| GET_CODE (orig) == LABEL_REF)
{
if (mode == HImode || mode == HFmode)
{
/* We need to force the address into
a register so that it is offsettable. */
rtx addr_reg = gen_reg_rtx (Pmode);
emit_move_insn (addr_reg, orig);
return addr_reg;
}
else
{
rtx dp_reg = gen_rtx_REG (Pmode, DP_REGNO);
if (! TARGET_SMALL)
emit_insn (gen_set_ldp (dp_reg, orig));
return gen_rtx_LO_SUM (Pmode, dp_reg, orig);
}
}
 
return NULL_RTX;
}
 
 
/* Provide the costs of an addressing mode that contains ADDR.
If ADDR is not a valid address, its cost is irrelevant.
This is used in cse and loop optimization to determine
if it is worthwhile storing a common address into a register.
Unfortunately, the C4x address cost depends on other operands. */
 
static int
c4x_address_cost (rtx addr)
{
switch (GET_CODE (addr))
{
case REG:
return 1;
 
case POST_INC:
case POST_DEC:
case PRE_INC:
case PRE_DEC:
return 1;
/* These shouldn't be directly generated. */
case SYMBOL_REF:
case LABEL_REF:
case CONST:
return 10;
 
case LO_SUM:
{
rtx op1 = XEXP (addr, 1);
 
if (GET_CODE (op1) == LABEL_REF || GET_CODE (op1) == SYMBOL_REF)
return TARGET_SMALL ? 3 : 4;
if (GET_CODE (op1) == CONST)
{
rtx offset = const0_rtx;
op1 = eliminate_constant_term (op1, &offset);
/* ??? These costs need rethinking... */
if (GET_CODE (op1) == LABEL_REF)
return 3;
if (GET_CODE (op1) != SYMBOL_REF)
return 4;
if (INTVAL (offset) == 0)
return 3;
 
return 4;
}
fatal_insn ("c4x_address_cost: Invalid addressing mode", addr);
}
break;
case PLUS:
{
register rtx op0 = XEXP (addr, 0);
register rtx op1 = XEXP (addr, 1);
if (GET_CODE (op0) != REG)
break;
switch (GET_CODE (op1))
{
default:
break;
 
case REG:
/* This cost for REG+REG must be greater than the cost
for REG if we want autoincrement addressing modes. */
return 2;
 
case CONST_INT:
/* The following tries to improve GIV combination
in strength reduce but appears not to help. */
if (TARGET_DEVEL && IS_UINT5_CONST (INTVAL (op1)))
return 1;
 
if (IS_DISP1_CONST (INTVAL (op1)))
return 1;
 
if (! TARGET_C3X && IS_UINT5_CONST (INTVAL (op1)))
return 2;
 
return 3;
}
}
default:
break;
}
return 4;
}
 
 
rtx
c4x_gen_compare_reg (enum rtx_code code, rtx x, rtx 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 (const char *form, rtx seq)
{
int delayed = 0;
int annultrue = 0;
int annulfalse = 0;
rtx delay;
char *cp;
static char str[100];
if (final_sequence)
{
delay = XVECEXP (final_sequence, 0, 1);
delayed = ! INSN_ANNULLED_BRANCH_P (seq);
annultrue = INSN_ANNULLED_BRANCH_P (seq) && ! INSN_FROM_TARGET_P (delay);
annulfalse = INSN_ANNULLED_BRANCH_P (seq) && INSN_FROM_TARGET_P (delay);
}
strcpy (str, form);
cp = &str [strlen (str)];
if (delayed)
{
*cp++ = '%';
*cp++ = '#';
}
if (annultrue)
{
*cp++ = 'a';
*cp++ = 't';
}
if (annulfalse)
{
*cp++ = 'a';
*cp++ = 'f';
}
*cp++ = '\t';
*cp++ = '%';
*cp++ = 'l';
*cp++ = '1';
*cp = 0;
return str;
}
 
void
c4x_print_operand (FILE *file, rtx op, int letter)
{
rtx op1;
enum rtx_code code;
 
switch (letter)
{
case '#': /* Delayed. */
if (final_sequence)
fprintf (file, "d");
return;
}
 
code = GET_CODE (op);
switch (letter)
{
case 'A': /* Direct address. */
if (code == CONST_INT || code == SYMBOL_REF || code == CONST)
fprintf (file, "@");
break;
 
case 'H': /* Sethi. */
output_addr_const (file, op);
return;
 
case 'I': /* Reversed condition. */
code = reverse_condition (code);
break;
 
case 'L': /* Log 2 of constant. */
if (code != CONST_INT)
fatal_insn ("c4x_print_operand: %%L inconsistency", op);
fprintf (file, "%d", exact_log2 (INTVAL (op)));
return;
 
case 'N': /* Ones complement of small constant. */
if (code != CONST_INT)
fatal_insn ("c4x_print_operand: %%N inconsistency", op);
fprintf (file, HOST_WIDE_INT_PRINT_DEC, ~INTVAL (op));
return;
 
case 'K': /* Generate ldp(k) if direct address. */
if (! TARGET_SMALL
&& code == MEM
&& GET_CODE (XEXP (op, 0)) == LO_SUM
&& GET_CODE (XEXP (XEXP (op, 0), 0)) == REG
&& REGNO (XEXP (XEXP (op, 0), 0)) == DP_REGNO)
{
op1 = XEXP (XEXP (op, 0), 1);
if (GET_CODE(op1) == CONST_INT || GET_CODE(op1) == SYMBOL_REF)
{
fprintf (file, "\t%s\t@", TARGET_C3X ? "ldp" : "ldpk");
output_address (XEXP (adjust_address (op, VOIDmode, 1), 0));
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))
{
fprintf (file, "%s\t@", TARGET_C3X ? "ldp" : "ldpk");
output_address (XEXP (op, 0));
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 (adjust_address (op, VOIDmode, 1), 0));
else if (code == REG)
fprintf (file, "%s", reg_names[REGNO (op) + 1]);
else
fatal_insn ("c4x_print_operand: %%O inconsistency", op);
return;
 
case 'C': /* Call. */
break;
 
case 'U': /* Call/callu. */
if (code != SYMBOL_REF)
fprintf (file, "u");
return;
 
default:
break;
}
switch (code)
{
case REG:
if (GET_MODE_CLASS (GET_MODE (op)) == MODE_FLOAT
&& ! TARGET_TI)
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[64];
real_to_decimal (str, CONST_DOUBLE_REAL_VALUE (op),
sizeof (str), 0, 1);
fprintf (file, "%s", str);
}
break;
case CONST_INT:
fprintf (file, HOST_WIDE_INT_PRINT_DEC, INTVAL (op));
break;
case NE:
fprintf (file, "ne");
break;
case EQ:
fprintf (file, "eq");
break;
case GE:
fprintf (file, "ge");
break;
 
case GT:
fprintf (file, "gt");
break;
 
case LE:
fprintf (file, "le");
break;
 
case LT:
fprintf (file, "lt");
break;
 
case GEU:
fprintf (file, "hs");
break;
 
case GTU:
fprintf (file, "hi");
break;
 
case LEU:
fprintf (file, "ls");
break;
 
case LTU:
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 *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++(" HOST_WIDE_INT_PRINT_DEC ")",
reg_names[REGNO (op0)], INTVAL (op1));
else if (GET_CODE (XEXP (addr, 1)) == PLUS && INTVAL (op1) < 0)
fprintf (file, "*%s--(" HOST_WIDE_INT_PRINT_DEC ")",
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(" HOST_WIDE_INT_PRINT_DEC ")",
reg_names[REGNO (op0)], INTVAL (op1));
else if (GET_CODE (XEXP (addr, 1)) == PLUS && INTVAL (op1) < 0)
fprintf (file, "*--%s(" HOST_WIDE_INT_PRINT_DEC ")",
reg_names[REGNO (op0)], -INTVAL (op1));
else if (GET_CODE (XEXP (addr, 1)) == MINUS && REG_P (op1))
fprintf (file, "*--%s(%s)", reg_names[REGNO (op0)],
reg_names[REGNO (op1)]);
else
fatal_insn ("c4x_print_operand_address: Bad pre_modify", addr);
}
break;
case PRE_INC:
fprintf (file, "*++%s", reg_names[REGNO (XEXP (addr, 0))]);
break;
 
case POST_DEC:
fprintf (file, "*%s--", reg_names[REGNO (XEXP (addr, 0))]);
break;
 
case PLUS: /* Indirect with displacement. */
{
rtx op0 = XEXP (addr, 0);
rtx op1 = XEXP (addr, 1);
 
if (REG_P (op0))
{
if (REG_P (op1))
{
if (IS_INDEX_REG (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(" HOST_WIDE_INT_PRINT_DEC ")",
reg_names[REGNO (op0)],
-INTVAL (op1)); /* Base - displacement. */
}
else
{
fprintf (file, "*+%s(" HOST_WIDE_INT_PRINT_DEC ")",
reg_names[REGNO (op0)],
INTVAL (op1)); /* Base + displacement. */
}
}
else
fatal_insn ("c4x_print_operand_address: Bad operand case", addr);
}
break;
 
case LO_SUM:
{
rtx op0 = XEXP (addr, 0);
rtx op1 = XEXP (addr, 1);
if (REG_P (op0) && REGNO (op0) == DP_REGNO)
c4x_print_operand_address (file, op1);
else
fatal_insn ("c4x_print_operand_address: Bad operand case", addr);
}
break;
 
case CONST:
case SYMBOL_REF:
case LABEL_REF:
fprintf (file, "@");
output_addr_const (file, addr);
break;
 
/* We shouldn't access CONST_INT addresses. */
case CONST_INT:
 
default:
fatal_insn ("c4x_print_operand_address: Bad operand case", addr);
break;
}
}
 
 
/* Return nonzero if the floating point operand will fit
in the immediate field. */
 
int
c4x_immed_float_p (rtx op)
{
long convval[2];
int exponent;
REAL_VALUE_TYPE r;
 
REAL_VALUE_FROM_CONST_DOUBLE (r, op);
if (GET_MODE (op) == HFmode)
REAL_VALUE_TO_TARGET_DOUBLE (r, convval);
else
{
REAL_VALUE_TO_TARGET_SINGLE (r, convval[0]);
convval[1] = 0;
}
 
/* Sign extend exponent. */
exponent = (((convval[0] >> 24) & 0xff) ^ 0x80) - 0x80;
if (exponent == -128)
return 1; /* 0.0 */
if ((convval[0] & 0x00000fff) != 0 || convval[1] != 0)
return 0; /* Precision doesn't fit. */
return (exponent <= 7) /* Positive exp. */
&& (exponent >= -7); /* Negative exp. */
}
 
 
/* The last instruction in a repeat block cannot be a Bcond, DBcound,
CALL, CALLCond, TRAPcond, RETIcond, RETScond, IDLE, RPTB or RPTS.
 
None of the last four instructions from the bottom of the block can
be a BcondD, BRD, DBcondD, RPTBD, LAJ, LAJcond, LATcond, BcondAF,
BcondAT or RETIcondD.
 
This routine scans the four previous insns for a jump insn, and if
one is found, returns 1 so that we bung in a nop instruction.
This simple minded strategy will add a nop, when it may not
be required. Say when there is a JUMP_INSN near the end of the
block that doesn't get converted into a delayed branch.
 
Note that we cannot have a call insn, since we don't generate
repeat loops with calls in them (although I suppose we could, but
there's no benefit.)
 
!!! FIXME. The rptb_top insn may be sucked into a SEQUENCE. */
 
int
c4x_rptb_nop_p (rtx insn)
{
rtx start_label;
int i;
 
/* Extract the start label from the jump pattern (rptb_end). */
start_label = XEXP (XEXP (SET_SRC (XVECEXP (PATTERN (insn), 0, 0)), 1), 0);
 
/* If there is a label at the end of the loop we must insert
a NOP. */
do {
insn = previous_insn (insn);
} while (GET_CODE (insn) == NOTE
|| GET_CODE (insn) == USE
|| GET_CODE (insn) == CLOBBER);
if (GET_CODE (insn) == CODE_LABEL)
return 1;
 
for (i = 0; i < 4; i++)
{
/* Search back for prev non-note and non-label insn. */
while (GET_CODE (insn) == NOTE || GET_CODE (insn) == CODE_LABEL
|| GET_CODE (insn) == USE || GET_CODE (insn) == CLOBBER)
{
if (insn == start_label)
return i == 0;
 
insn = previous_insn (insn);
};
 
/* If we have a jump instruction we should insert a NOP. If we
hit repeat block top we should only insert a NOP if the loop
is empty. */
if (GET_CODE (insn) == JUMP_INSN)
return 1;
insn = previous_insn (insn);
}
return 0;
}
 
 
/* The C4x looping instruction needs to be emitted at the top of the
loop. Emitting the true RTL for a looping instruction at the top of
the loop can cause problems with flow analysis. So instead, a dummy
doloop insn is emitted at the end of the loop. This routine checks
for the presence of this doloop insn and then searches back to the
top of the loop, where it inserts the true looping insn (provided
there are no instructions in the loop which would cause problems).
Any additional labels can be emitted at this point. In addition, if
the desired loop count register was not allocated, this routine does
nothing.
 
Before we can create a repeat block looping instruction we have to
verify that there are no jumps outside the loop and no jumps outside
the loop go into this loop. This can happen in the basic blocks reorder
pass. The C4x cpu cannot handle this. */
 
static int
c4x_label_ref_used_p (rtx x, rtx code_label)
{
enum rtx_code code;
int i, j;
const char *fmt;
 
if (x == 0)
return 0;
 
code = GET_CODE (x);
if (code == LABEL_REF)
return INSN_UID (XEXP (x,0)) == INSN_UID (code_label);
 
fmt = GET_RTX_FORMAT (code);
for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
{
if (fmt[i] == 'e')
{
if (c4x_label_ref_used_p (XEXP (x, i), code_label))
return 1;
}
else if (fmt[i] == 'E')
for (j = XVECLEN (x, i) - 1; j >= 0; j--)
if (c4x_label_ref_used_p (XVECEXP (x, i, j), code_label))
return 1;
}
return 0;
}
 
 
static int
c4x_rptb_valid_p (rtx insn, rtx start_label)
{
rtx end = insn;
rtx start;
rtx tmp;
 
/* Find the start label. */
for (; insn; insn = PREV_INSN (insn))
if (insn == start_label)
break;
 
/* Note found then we cannot use a rptb or rpts. The label was
probably moved by the basic block reorder pass. */
if (! insn)
return 0;
 
start = insn;
/* If any jump jumps inside this block then we must fail. */
for (insn = PREV_INSN (start); insn; insn = PREV_INSN (insn))
{
if (GET_CODE (insn) == CODE_LABEL)
{
for (tmp = NEXT_INSN (start); tmp != end; tmp = NEXT_INSN(tmp))
if (GET_CODE (tmp) == JUMP_INSN
&& c4x_label_ref_used_p (tmp, insn))
return 0;
}
}
for (insn = NEXT_INSN (end); insn; insn = NEXT_INSN (insn))
{
if (GET_CODE (insn) == CODE_LABEL)
{
for (tmp = NEXT_INSN (start); tmp != end; tmp = NEXT_INSN(tmp))
if (GET_CODE (tmp) == JUMP_INSN
&& c4x_label_ref_used_p (tmp, insn))
return 0;
}
}
/* If any jump jumps outside this block then we must fail. */
for (insn = NEXT_INSN (start); insn != end; insn = NEXT_INSN (insn))
{
if (GET_CODE (insn) == CODE_LABEL)
{
for (tmp = NEXT_INSN (end); tmp; tmp = NEXT_INSN(tmp))
if (GET_CODE (tmp) == JUMP_INSN
&& c4x_label_ref_used_p (tmp, insn))
return 0;
for (tmp = PREV_INSN (start); tmp; tmp = PREV_INSN(tmp))
if (GET_CODE (tmp) == JUMP_INSN
&& c4x_label_ref_used_p (tmp, insn))
return 0;
}
}
 
/* All checks OK. */
return 1;
}
 
 
void
c4x_rptb_insert (rtx insn)
{
rtx end_label;
rtx start_label;
rtx new_start_label;
rtx count_reg;
 
/* If the count register has not been allocated to RC, say if
there is a movmem pattern in the loop, then do not insert a
RPTB instruction. Instead we emit a decrement and branch
at the end of the loop. */
count_reg = XEXP (XEXP (SET_SRC (XVECEXP (PATTERN (insn), 0, 0)), 0), 0);
if (REGNO (count_reg) != RC_REGNO)
return;
 
/* Extract the start label from the jump pattern (rptb_end). */
start_label = XEXP (XEXP (SET_SRC (XVECEXP (PATTERN (insn), 0, 0)), 1), 0);
if (! c4x_rptb_valid_p (insn, start_label))
{
/* We cannot use the rptb insn. Replace it so reorg can use
the delay slots of the jump insn. */
emit_insn_before (gen_addqi3 (count_reg, count_reg, constm1_rtx), insn);
emit_insn_before (gen_cmpqi (count_reg, const0_rtx), insn);
emit_insn_before (gen_bge (start_label), insn);
LABEL_NUSES (start_label)++;
delete_insn (insn);
return;
}
 
end_label = gen_label_rtx ();
LABEL_NUSES (end_label)++;
emit_label_after (end_label, insn);
 
new_start_label = gen_label_rtx ();
LABEL_NUSES (new_start_label)++;
 
for (; insn; insn = PREV_INSN (insn))
{
if (insn == start_label)
break;
if (GET_CODE (insn) == JUMP_INSN &&
JUMP_LABEL (insn) == start_label)
redirect_jump (insn, new_start_label, 0);
}
if (! insn)
fatal_insn ("c4x_rptb_insert: Cannot find start label", start_label);
 
emit_label_after (new_start_label, insn);
 
if (TARGET_RPTS && c4x_rptb_rpts_p (PREV_INSN (insn), 0))
emit_insn_after (gen_rpts_top (new_start_label, end_label), insn);
else
emit_insn_after (gen_rptb_top (new_start_label, end_label), insn);
if (LABEL_NUSES (start_label) == 0)
delete_insn (start_label);
}
 
 
/* 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 optimization 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.
 
This function also fixes up RTPB style loops that didn't get RC
allocated as the loop counter. */
 
static void
c4x_reorg (void)
{
rtx insn;
 
for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
{
/* Look for insn. */
if (INSN_P (insn))
{
int insn_code_number;
rtx old;
 
insn_code_number = recog_memoized (insn);
 
if (insn_code_number < 0)
continue;
 
/* Insert the RTX for RPTB at the top of the loop
and a label at the end of the loop. */
if (insn_code_number == CODE_FOR_rptb_end)
c4x_rptb_insert(insn);
 
/* We need to split the insn here. Otherwise the calls to
force_const_mem will not work for load_immed_address. */
old = insn;
 
/* Don't split the insn if it has been deleted. */
if (! INSN_DELETED_P (old))
insn = try_split (PATTERN(old), old, 1);
 
/* When not optimizing, the old insn will be still left around
with only the 'deleted' bit set. Transform it into a note
to avoid confusion of subsequent processing. */
if (INSN_DELETED_P (old))
{
PUT_CODE (old, NOTE);
NOTE_LINE_NUMBER (old) = NOTE_INSN_DELETED;
NOTE_SOURCE_FILE (old) = 0;
}
}
}
}
 
 
int
c4x_a_register (rtx op)
{
return REG_P (op) && IS_ADDR_OR_PSEUDO_REG (op);
}
 
 
int
c4x_x_register (rtx op)
{
return REG_P (op) && IS_INDEX_OR_PSEUDO_REG (op);
}
 
 
static int
c4x_immed_int_constant (rtx op)
{
if (GET_CODE (op) != CONST_INT)
return 0;
 
return GET_MODE (op) == VOIDmode
|| GET_MODE_CLASS (GET_MODE (op)) == MODE_INT
|| GET_MODE_CLASS (GET_MODE (op)) == MODE_PARTIAL_INT;
}
 
 
static int
c4x_immed_float_constant (rtx op)
{
if (GET_CODE (op) != CONST_DOUBLE)
return 0;
 
/* Do not check if the CONST_DOUBLE is in memory. If there is a MEM
present this only means that a MEM rtx has been generated. It does
not mean the rtx is really in memory. */
 
return GET_MODE (op) == QFmode || GET_MODE (op) == HFmode;
}
 
 
int
c4x_shiftable_constant (rtx op)
{
int i;
int mask;
int val = INTVAL (op);
 
for (i = 0; i < 16; i++)
{
if (val & (1 << i))
break;
}
mask = ((0xffff >> i) << 16) | 0xffff;
if (IS_INT16_CONST (val & (1 << 31) ? (val >> i) | ~mask
: (val >> i) & mask))
return i;
return -1;
}
 
 
int
c4x_H_constant (rtx op)
{
return c4x_immed_float_constant (op) && c4x_immed_float_p (op);
}
 
 
int
c4x_I_constant (rtx op)
{
return c4x_immed_int_constant (op) && IS_INT16_CONST (INTVAL (op));
}
 
 
int
c4x_J_constant (rtx op)
{
if (TARGET_C3X)
return 0;
return c4x_immed_int_constant (op) && IS_INT8_CONST (INTVAL (op));
}
 
 
int
c4x_K_constant (rtx op)
{
if (TARGET_C3X || ! c4x_immed_int_constant (op))
return 0;
return IS_INT5_CONST (INTVAL (op));
}
 
 
int
c4x_L_constant (rtx op)
{
return c4x_immed_int_constant (op) && IS_UINT16_CONST (INTVAL (op));
}
 
 
int
c4x_N_constant (rtx op)
{
return c4x_immed_int_constant (op) && IS_NOT_UINT16_CONST (INTVAL (op));
}
 
 
int
c4x_O_constant (rtx op)
{
return c4x_immed_int_constant (op) && IS_HIGH_CONST (INTVAL (op));
}
 
 
/* The constraints do not have to check the register class,
except when needed to discriminate between the constraints.
The operand has been checked by the predicates to be valid. */
 
/* ARx + 9-bit signed const or IRn
*ARx, *+ARx(n), *-ARx(n), *+ARx(IRn), *-Arx(IRn) for -256 < n < 256
We don't include the pre/post inc/dec forms here since
they are handled by the <> constraints. */
 
int
c4x_Q_constraint (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 (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 (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_REG (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_REG (op0)
&& GET_CODE (op1) == CONST_INT
&& IS_UINT5_CONST (INTVAL (op1) + 1);
 
return REG_P (op0)
&& IS_ADDR_OR_PSEUDO_REG (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 (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;
}
 
 
int
c4x_S_indirect (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_REG (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_REG (op0)
&& REG_P (op1) && IS_INDEX_OR_PSEUDO_REG (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_REG (op0)
&& GET_CODE (op1) == CONST_INT
&& IS_DISP1_OFF_CONST (INTVAL (op1));
 
if (REG_P (op1))
return (IS_INDEX_OR_PSEUDO_REG (op1)
&& IS_ADDR_OR_PSEUDO_REG (op0))
|| (IS_ADDR_OR_PSEUDO_REG (op1)
&& IS_INDEX_OR_PSEUDO_REG (op0));
return IS_ADDR_OR_PSEUDO_REG (op0)
&& GET_CODE (op1) == CONST_INT
&& IS_DISP1_CONST (INTVAL (op1));
}
}
break;
 
default:
break;
}
return 0;
}
 
 
/* Direct memory operand. */
 
int
c4x_T_constraint (rtx op)
{
if (GET_CODE (op) != MEM)
return 0;
op = XEXP (op, 0);
 
if (GET_CODE (op) != LO_SUM)
{
/* Allow call operands. */
return GET_CODE (op) == SYMBOL_REF
&& GET_MODE (op) == Pmode
&& SYMBOL_REF_FUNCTION_P (op);
}
 
/* HImode and HFmode are not offsettable. */
if (GET_MODE (op) == HImode || GET_CODE (op) == HFmode)
return 0;
 
if ((GET_CODE (XEXP (op, 0)) == REG)
&& (REGNO (XEXP (op, 0)) == DP_REGNO))
return c4x_U_constraint (XEXP (op, 1));
return 0;
}
 
 
/* Symbolic operand. */
 
int
c4x_U_constraint (rtx op)
{
/* Don't allow direct addressing to an arbitrary constant. */
return GET_CODE (op) == CONST
|| GET_CODE (op) == SYMBOL_REF
|| GET_CODE (op) == LABEL_REF;
}
 
 
int
c4x_autoinc_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
{
if (GET_CODE (op) == MEM)
{
enum rtx_code code = GET_CODE (XEXP (op, 0));
if (code == PRE_INC
|| code == PRE_DEC
|| code == POST_INC
|| code == POST_DEC
|| code == PRE_MODIFY
|| code == POST_MODIFY
)
return 1;
}
return 0;
}
 
 
int
mixed_subreg_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
{
/* Allow (subreg:HF (reg:HI)) that be generated for a union of an
int and a long double. */
if (GET_CODE (op) == SUBREG
&& (GET_MODE (op) == QFmode)
&& (GET_MODE (SUBREG_REG (op)) == QImode
|| GET_MODE (SUBREG_REG (op)) == HImode))
return 1;
return 0;
}
 
 
int
reg_imm_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
{
if (REG_P (op) || CONSTANT_P (op))
return 1;
return 0;
}
 
 
int
not_modify_reg (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
{
if (REG_P (op) || CONSTANT_P (op))
return 1;
if (GET_CODE (op) != MEM)
return 0;
op = XEXP (op, 0);
switch (GET_CODE (op))
{
case REG:
return 1;
 
case PLUS:
{
rtx op0 = XEXP (op, 0);
rtx op1 = XEXP (op, 1);
 
if (! REG_P (op0))
return 0;
if (REG_P (op1) || GET_CODE (op1) == CONST_INT)
return 1;
}
 
case LO_SUM:
{
rtx op0 = XEXP (op, 0);
if (REG_P (op0) && REGNO (op0) == DP_REGNO)
return 1;
}
break;
case CONST:
case SYMBOL_REF:
case LABEL_REF:
return 1;
 
default:
break;
}
return 0;
}
 
 
int
not_rc_reg (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
{
if (REG_P (op) && REGNO (op) == RC_REGNO)
return 0;
return 1;
}
 
 
static void
c4x_S_address_parse (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;
}
}
/* Fall through. */
 
default:
fatal_insn ("invalid indirect (S) memory address", op);
}
}
 
 
int
c4x_address_conflict (rtx op0, rtx op1, int store0, int store1)
{
int base0;
int base1;
int incdec0;
int incdec1;
int index0;
int index1;
int disp0;
int disp1;
if (MEM_VOLATILE_P (op0) && MEM_VOLATILE_P (op1))
return 1;
 
c4x_S_address_parse (op0, &base0, &incdec0, &index0, &disp0);
c4x_S_address_parse (op1, &base1, &incdec1, &index1, &disp1);
 
if (store0 && store1)
{
/* If we have two stores in parallel to the same address, then
the C4x only executes one of the stores. This is unlikely to
cause problems except when writing to a hardware device such
as a FIFO since the second write will be lost. The user
should flag the hardware location as being volatile so that
we don't do this optimization. While it is unlikely that we
have an aliased address if both locations are not marked
volatile, it is probably safer to flag a potential conflict
if either location is volatile. */
if (! flag_argument_noalias)
{
if (MEM_VOLATILE_P (op0) || MEM_VOLATILE_P (op1))
return 1;
}
}
 
/* If have a parallel load and a store to the same address, the load
is performed first, so there is no conflict. Similarly, there is
no conflict if have parallel loads from the same address. */
 
/* Cannot use auto increment or auto decrement twice for same
base register. */
if (base0 == base1 && incdec0 && incdec0)
return 1;
 
/* It might be too confusing for GCC if we have use a base register
with a side effect and a memory reference using the same register
in parallel. */
if (! TARGET_DEVEL && base0 == base1 && (incdec0 || incdec1))
return 1;
 
/* We cannot optimize the case where op1 and op2 refer to the same
address. */
if (base0 == base1 && disp0 == disp1 && index0 == index1)
return 1;
 
/* No conflict. */
return 0;
}
 
 
/* Check for while loop inside a decrement and branch loop. */
 
int
c4x_label_conflict (rtx insn, rtx jump, rtx db)
{
while (insn)
{
if (GET_CODE (insn) == CODE_LABEL)
{
if (CODE_LABEL_NUMBER (jump) == CODE_LABEL_NUMBER (insn))
return 1;
if (CODE_LABEL_NUMBER (db) == CODE_LABEL_NUMBER (insn))
return 0;
}
insn = PREV_INSN (insn);
}
return 1;
}
 
 
/* Validate combination of operands for parallel load/store instructions. */
 
int
valid_parallel_load_store (rtx *operands,
enum machine_mode mode ATTRIBUTE_UNUSED)
{
rtx op0 = operands[0];
rtx op1 = operands[1];
rtx op2 = operands[2];
rtx op3 = operands[3];
 
if (GET_CODE (op0) == SUBREG)
op0 = SUBREG_REG (op0);
if (GET_CODE (op1) == SUBREG)
op1 = SUBREG_REG (op1);
if (GET_CODE (op2) == SUBREG)
op2 = SUBREG_REG (op2);
if (GET_CODE (op3) == SUBREG)
op3 = SUBREG_REG (op3);
 
/* The patterns should only allow ext_low_reg_operand() or
par_ind_operand() operands. Thus of the 4 operands, only 2
should be REGs and the other 2 should be MEMs. */
 
/* This test prevents the multipack pass from using this pattern if
op0 is used as an index or base register in op2 or op3, since
this combination will require reloading. */
if (GET_CODE (op0) == REG
&& ((GET_CODE (op2) == MEM && reg_mentioned_p (op0, XEXP (op2, 0)))
|| (GET_CODE (op3) == MEM && reg_mentioned_p (op0, XEXP (op3, 0)))))
return 0;
 
/* LDI||LDI. */
if (GET_CODE (op0) == REG && GET_CODE (op2) == REG)
return (REGNO (op0) != REGNO (op2))
&& GET_CODE (op1) == MEM && GET_CODE (op3) == MEM
&& ! c4x_address_conflict (op1, op3, 0, 0);
 
/* STI||STI. */
if (GET_CODE (op1) == REG && GET_CODE (op3) == REG)
return GET_CODE (op0) == MEM && GET_CODE (op2) == MEM
&& ! c4x_address_conflict (op0, op2, 1, 1);
 
/* LDI||STI. */
if (GET_CODE (op0) == REG && GET_CODE (op3) == REG)
return GET_CODE (op1) == MEM && GET_CODE (op2) == MEM
&& ! c4x_address_conflict (op1, op2, 0, 1);
 
/* STI||LDI. */
if (GET_CODE (op1) == REG && GET_CODE (op2) == REG)
return GET_CODE (op0) == MEM && GET_CODE (op3) == MEM
&& ! c4x_address_conflict (op0, op3, 1, 0);
 
return 0;
}
 
 
int
valid_parallel_operands_4 (rtx *operands,
enum machine_mode mode ATTRIBUTE_UNUSED)
{
rtx op0 = operands[0];
rtx op2 = operands[2];
 
if (GET_CODE (op0) == SUBREG)
op0 = SUBREG_REG (op0);
if (GET_CODE (op2) == SUBREG)
op2 = SUBREG_REG (op2);
 
/* This test prevents the multipack pass from using this pattern if
op0 is used as an index or base register in op2, since this combination
will require reloading. */
if (GET_CODE (op0) == REG
&& GET_CODE (op2) == MEM
&& reg_mentioned_p (op0, XEXP (op2, 0)))
return 0;
 
return 1;
}
 
 
int
valid_parallel_operands_5 (rtx *operands,
enum machine_mode mode ATTRIBUTE_UNUSED)
{
int regs = 0;
rtx op0 = operands[0];
rtx op1 = operands[1];
rtx op2 = operands[2];
rtx op3 = operands[3];
 
if (GET_CODE (op0) == SUBREG)
op0 = SUBREG_REG (op0);
if (GET_CODE (op1) == SUBREG)
op1 = SUBREG_REG (op1);
if (GET_CODE (op2) == SUBREG)
op2 = SUBREG_REG (op2);
 
/* The patterns should only allow ext_low_reg_operand() or
par_ind_operand() operands. Operands 1 and 2 may be commutative
but only one of them can be a register. */
if (GET_CODE (op1) == REG)
regs++;
if (GET_CODE (op2) == REG)
regs++;
 
if (regs != 1)
return 0;
 
/* This test prevents the multipack pass from using this pattern if
op0 is used as an index or base register in op3, since this combination
will require reloading. */
if (GET_CODE (op0) == REG
&& GET_CODE (op3) == MEM
&& reg_mentioned_p (op0, XEXP (op3, 0)))
return 0;
 
return 1;
}
 
 
int
valid_parallel_operands_6 (rtx *operands,
enum machine_mode mode ATTRIBUTE_UNUSED)
{
int regs = 0;
rtx op0 = operands[0];
rtx op1 = operands[1];
rtx op2 = operands[2];
rtx op4 = operands[4];
rtx op5 = operands[5];
 
if (GET_CODE (op1) == SUBREG)
op1 = SUBREG_REG (op1);
if (GET_CODE (op2) == SUBREG)
op2 = SUBREG_REG (op2);
if (GET_CODE (op4) == SUBREG)
op4 = SUBREG_REG (op4);
if (GET_CODE (op5) == SUBREG)
op5 = SUBREG_REG (op5);
 
/* The patterns should only allow ext_low_reg_operand() or
par_ind_operand() operands. Thus of the 4 input operands, only 2
should be REGs and the other 2 should be MEMs. */
 
if (GET_CODE (op1) == REG)
regs++;
if (GET_CODE (op2) == REG)
regs++;
if (GET_CODE (op4) == REG)
regs++;
if (GET_CODE (op5) == REG)
regs++;
 
/* The new C30/C40 silicon dies allow 3 regs of the 4 input operands.
Perhaps we should count the MEMs as well? */
if (regs != 2)
return 0;
 
/* This test prevents the multipack pass from using this pattern if
op0 is used as an index or base register in op4 or op5, since
this combination will require reloading. */
if (GET_CODE (op0) == REG
&& ((GET_CODE (op4) == MEM && reg_mentioned_p (op0, XEXP (op4, 0)))
|| (GET_CODE (op5) == MEM && reg_mentioned_p (op0, XEXP (op5, 0)))))
return 0;
 
return 1;
}
 
 
/* Validate combination of src operands. Note that the operands have
been screened by the src_operand predicate. We just have to check
that the combination of operands is valid. If FORCE is set, ensure
that the destination regno is valid if we have a 2 operand insn. */
 
static int
c4x_valid_operands (enum rtx_code code, rtx *operands,
enum machine_mode mode ATTRIBUTE_UNUSED,
int force)
{
rtx op0;
rtx op1;
rtx op2;
enum rtx_code code1;
enum rtx_code code2;
 
 
/* FIXME, why can't we tighten the operands for IF_THEN_ELSE? */
if (code == IF_THEN_ELSE)
return 1 || (operands[0] == operands[2] || operands[0] == operands[3]);
 
if (code == COMPARE)
{
op1 = operands[0];
op2 = operands[1];
}
else
{
op1 = operands[1];
op2 = operands[2];
}
 
op0 = operands[0];
 
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);
 
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) && c4x_S_indirect (op2))
return 1;
return c4x_R_indirect (op1) && c4x_R_indirect (op2);
}
 
/* We cannot handle two MEMs or two CONSTS, etc. */
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:
break;
default:
fatal_insn ("c4x_valid_operands: Internal error", op2);
break;
}
if (GET_CODE (op0) == SCRATCH)
return 1;
 
if (!REG_P (op0))
return 0;
 
/* Check that we have a valid destination register for a two operand
instruction. */
return ! force || code == COMPARE || REGNO (op1) == REGNO (op0);
}
 
 
/* Check non-commutative operators. */
if (code == ASHIFTRT || code == LSHIFTRT
|| code == ASHIFT || code == COMPARE)
return code2 == REG
&& (c4x_S_indirect (op1) || c4x_R_indirect (op1));
 
 
/* 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 (code2 == REG)
{
switch (code1)
{
case CONST_INT:
break;
case CONST_DOUBLE:
if (! c4x_H_constant (op1))
return 0;
break;
 
/* Any valid memory operand screened by src_operand is OK. */
case MEM:
break;
default:
abort ();
break;
}
 
if (GET_CODE (op0) == SCRATCH)
return 1;
 
if (!REG_P (op0))
return 0;
 
/* Check that we have a valid destination register for a two operand
instruction. */
return ! force || REGNO (op1) == REGNO (op0);
}
if (c4x_J_constant (op1) && c4x_R_indirect (op2))
return 1;
 
return 0;
}
 
 
int valid_operands (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 (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
optimization 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
&& rtx_cost (operands[1], code) > 1)
operands[1] = force_reg (mode, operands[1]);
if (! reload_in_progress
&& ! c4x_valid_operands (code, operands, mode, 0))
operands[0] = force_reg (mode, operands[0]);
return 1;
}
/* We cannot do this for ADDI/SUBI insns since we will
defeat the flow pass from finding autoincrement addressing
opportunities. */
if (! reload_in_progress
&& ! ((code == PLUS || code == MINUS) && mode == Pmode)
&& TARGET_HOIST
&& optimize > 1
&& GET_CODE (operands[2]) == CONST_INT
&& rtx_cost (operands[2], code) > 1)
operands[2] = force_reg (mode, operands[2]);
 
/* We can get better code on a C30 if we force constant shift counts
into a register. This way they can get hoisted out of loops,
tying up a register but saving an instruction. The downside is
that they may get allocated to an address or index register, and
thus we will get a pipeline conflict if there is a nearby
indirect address using an address register.
 
Note that expand_binops will not try to load an expensive constant
into a register if it is used within a loop for a shift insn. */
if (! reload_in_progress
&& ! c4x_valid_operands (code, operands, mode, TARGET_FORCE))
{
/* If the operand combination is invalid, we force operand1 into a
register, preventing reload from having doing to do this at a
later stage. */
operands[1] = force_reg (mode, operands[1]);
if (TARGET_FORCE)
{
emit_move_insn (operands[0], operands[1]);
operands[1] = copy_rtx (operands[0]);
}
else
{
/* Just in case... */
if (! c4x_valid_operands (code, operands, mode, 0))
operands[2] = force_reg (mode, operands[2]);
}
}
 
/* Right shifts require a negative shift count, but GCC expects
a positive count, so we emit a NEG. */
if ((code == ASHIFTRT || code == LSHIFTRT)
&& (GET_CODE (operands[2]) != CONST_INT))
operands[2] = gen_rtx_NEG (mode, negate_rtx (mode, operands[2]));
 
/* When the shift count is greater than 32 then the result
can be implementation dependent. We truncate the result to
fit in 5 bits so that we do not emit invalid code when
optimizing---such as trying to generate lhu2 with 20021124-1.c. */
if (((code == ASHIFTRT || code == LSHIFTRT || code == ASHIFT)
&& (GET_CODE (operands[2]) == CONST_INT))
&& INTVAL (operands[2]) > (GET_MODE_BITSIZE (mode) - 1))
operands[2]
= GEN_INT (INTVAL (operands[2]) & (GET_MODE_BITSIZE (mode) - 1));
 
return 1;
}
 
 
/* The following predicates are used for instruction scheduling. */
 
int
group1_reg_operand (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) && (! reload_completed || IS_GROUP1_REG (op));
}
 
 
int
group1_mem_operand (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 ((REG_P (op0) && (! reload_completed || IS_GROUP1_REG (op0)))
|| (REG_P (op1) && (! reload_completed || IS_GROUP1_REG (op1))))
return 1;
}
else if ((REG_P (op)) && (! reload_completed || IS_GROUP1_REG (op)))
return 1;
}
 
return 0;
}
 
 
/* Return true if any one of the address registers. */
 
int
arx_reg_operand (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) && (! reload_completed || IS_ADDR_REG (op));
}
 
 
static int
c4x_arn_reg_operand (rtx op, enum machine_mode mode, unsigned int regno)
{
if (mode != VOIDmode && mode != GET_MODE (op))
return 0;
if (GET_CODE (op) == SUBREG)
op = SUBREG_REG (op);
return REG_P (op) && (! reload_completed || (REGNO (op) == regno));
}
 
 
static int
c4x_arn_mem_operand (rtx op, enum machine_mode mode, unsigned 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:
return REG_P (op) && (! reload_completed || (REGNO (op) == regno));
 
case PRE_MODIFY:
case POST_MODIFY:
if (REG_P (XEXP (op, 0)) && (! reload_completed
|| (REGNO (XEXP (op, 0)) == regno)))
return 1;
if (REG_P (XEXP (XEXP (op, 1), 1))
&& (! reload_completed
|| (REGNO (XEXP (XEXP (op, 1), 1)) == regno)))
return 1;
break;
 
case PLUS:
{
rtx op0 = XEXP (op, 0);
rtx op1 = XEXP (op, 1);
 
if ((REG_P (op0) && (! reload_completed
|| (REGNO (op0) == regno)))
|| (REG_P (op1) && (! reload_completed
|| (REGNO (op1) == regno))))
return 1;
}
break;
 
default:
break;
}
}
return 0;
}
 
 
int
ar0_reg_operand (rtx op, enum machine_mode mode)
{
return c4x_arn_reg_operand (op, mode, AR0_REGNO);
}
 
 
int
ar0_mem_operand (rtx op, enum machine_mode mode)
{
return c4x_arn_mem_operand (op, mode, AR0_REGNO);
}
 
 
int
ar1_reg_operand (rtx op, enum machine_mode mode)
{
return c4x_arn_reg_operand (op, mode, AR1_REGNO);
}
 
 
int
ar1_mem_operand (rtx op, enum machine_mode mode)
{
return c4x_arn_mem_operand (op, mode, AR1_REGNO);
}
 
 
int
ar2_reg_operand (rtx op, enum machine_mode mode)
{
return c4x_arn_reg_operand (op, mode, AR2_REGNO);
}
 
 
int
ar2_mem_operand (rtx op, enum machine_mode mode)
{
return c4x_arn_mem_operand (op, mode, AR2_REGNO);
}
 
 
int
ar3_reg_operand (rtx op, enum machine_mode mode)
{
return c4x_arn_reg_operand (op, mode, AR3_REGNO);
}
 
 
int
ar3_mem_operand (rtx op, enum machine_mode mode)
{
return c4x_arn_mem_operand (op, mode, AR3_REGNO);
}
 
 
int
ar4_reg_operand (rtx op, enum machine_mode mode)
{
return c4x_arn_reg_operand (op, mode, AR4_REGNO);
}
 
 
int
ar4_mem_operand (rtx op, enum machine_mode mode)
{
return c4x_arn_mem_operand (op, mode, AR4_REGNO);
}
 
 
int
ar5_reg_operand (rtx op, enum machine_mode mode)
{
return c4x_arn_reg_operand (op, mode, AR5_REGNO);
}
 
 
int
ar5_mem_operand (rtx op, enum machine_mode mode)
{
return c4x_arn_mem_operand (op, mode, AR5_REGNO);
}
 
 
int
ar6_reg_operand (rtx op, enum machine_mode mode)
{
return c4x_arn_reg_operand (op, mode, AR6_REGNO);
}
 
 
int
ar6_mem_operand (rtx op, enum machine_mode mode)
{
return c4x_arn_mem_operand (op, mode, AR6_REGNO);
}
 
 
int
ar7_reg_operand (rtx op, enum machine_mode mode)
{
return c4x_arn_reg_operand (op, mode, AR7_REGNO);
}
 
 
int
ar7_mem_operand (rtx op, enum machine_mode mode)
{
return c4x_arn_mem_operand (op, mode, AR7_REGNO);
}
 
 
int
ir0_reg_operand (rtx op, enum machine_mode mode)
{
return c4x_arn_reg_operand (op, mode, IR0_REGNO);
}
 
 
int
ir0_mem_operand (rtx op, enum machine_mode mode)
{
return c4x_arn_mem_operand (op, mode, IR0_REGNO);
}
 
 
int
ir1_reg_operand (rtx op, enum machine_mode mode)
{
return c4x_arn_reg_operand (op, mode, IR1_REGNO);
}
 
 
int
ir1_mem_operand (rtx op, enum machine_mode mode)
{
return c4x_arn_mem_operand (op, mode, IR1_REGNO);
}
 
 
/* This is similar to operand_subword but allows autoincrement
addressing. */
 
rtx
c4x_operand_subword (rtx op, int i, int validate_address,
enum machine_mode mode)
{
if (mode != HImode && mode != HFmode)
fatal_insn ("c4x_operand_subword: invalid mode", op);
 
if (mode == HFmode && REG_P (op))
fatal_insn ("c4x_operand_subword: invalid operand", op);
 
if (GET_CODE (op) == MEM)
{
enum rtx_code code = GET_CODE (XEXP (op, 0));
enum machine_mode mode = GET_MODE (XEXP (op, 0));
enum machine_mode submode;
 
submode = mode;
if (mode == HImode)
submode = QImode;
else if (mode == HFmode)
submode = QFmode;
 
switch (code)
{
case POST_INC:
case PRE_INC:
return gen_rtx_MEM (submode, XEXP (op, 0));
case POST_DEC:
case PRE_DEC:
case PRE_MODIFY:
case POST_MODIFY:
/* We could handle these with some difficulty.
e.g., *p-- => *(p-=2); *(p+1). */
fatal_insn ("c4x_operand_subword: invalid autoincrement", op);
 
case SYMBOL_REF:
case LABEL_REF:
case CONST:
case CONST_INT:
fatal_insn ("c4x_operand_subword: invalid address", op);
 
/* Even though offsettable_address_p considers (MEM
(LO_SUM)) to be offsettable, it is not safe if the
address is at the end of the data page since we also have
to fix up the associated high PART. In this case where
we are trying to split a HImode or HFmode memory
reference, we would have to emit another insn to reload a
new HIGH value. It's easier to disable LO_SUM memory references
in HImode or HFmode and we probably get better code. */
case LO_SUM:
fatal_insn ("c4x_operand_subword: address not offsettable", op);
default:
break;
}
}
return operand_subword (op, i, validate_address, mode);
}
 
struct name_list
{
struct name_list *next;
const char *name;
};
 
static struct name_list *global_head;
static struct name_list *extern_head;
 
 
/* Add NAME to list of global symbols and remove from external list if
present on external list. */
 
void
c4x_global_label (const char *name)
{
struct name_list *p, *last;
 
/* Do not insert duplicate names, so linearly search through list of
existing names. */
p = global_head;
while (p)
{
if (strcmp (p->name, name) == 0)
return;
p = p->next;
}
p = (struct name_list *) xmalloc (sizeof *p);
p->next = global_head;
p->name = name;
global_head = p;
 
/* Remove this name from ref list if present. */
last = NULL;
p = extern_head;
while (p)
{
if (strcmp (p->name, name) == 0)
{
if (last)
last->next = p->next;
else
extern_head = p->next;
break;
}
last = p;
p = p->next;
}
}
 
 
/* Add NAME to list of external symbols. */
 
void
c4x_external_ref (const char *name)
{
struct name_list *p;
 
/* Do not insert duplicate names. */
p = extern_head;
while (p)
{
if (strcmp (p->name, name) == 0)
return;
p = p->next;
}
/* Do not insert ref if global found. */
p = global_head;
while (p)
{
if (strcmp (p->name, name) == 0)
return;
p = p->next;
}
p = (struct name_list *) xmalloc (sizeof *p);
p->next = extern_head;
p->name = name;
extern_head = p;
}
 
/* 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. */
static void
c4x_file_start (void)
{
default_file_start ();
fprintf (asm_out_file, "\t.version\t%d\n", c4x_cpu_version);
fputs ("\n\t.data\ndata_sec:\n", asm_out_file);
}
 
 
static void
c4x_file_end (void)
{
struct name_list *p;
/* Output all external names that are not global. */
p = extern_head;
while (p)
{
fprintf (asm_out_file, "\t.ref\t");
assemble_name (asm_out_file, p->name);
fprintf (asm_out_file, "\n");
p = p->next;
}
fprintf (asm_out_file, "\t.end\n");
}
 
 
static void
c4x_check_attribute (const char *attrib, tree list, tree decl, tree *attributes)
{
while (list != NULL_TREE
&& IDENTIFIER_POINTER (TREE_PURPOSE (list))
!= IDENTIFIER_POINTER (DECL_NAME (decl)))
list = TREE_CHAIN (list);
if (list)
*attributes = tree_cons (get_identifier (attrib), TREE_VALUE (list),
*attributes);
}
 
 
static void
c4x_insert_attributes (tree decl, tree *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);
c4x_check_attribute ("naked", naked_tree, decl, attributes);
break;
 
case VAR_DECL:
c4x_check_attribute ("section", data_tree, decl, attributes);
break;
 
default:
break;
}
}
 
/* Table of valid machine attributes. */
const struct attribute_spec c4x_attribute_table[] =
{
/* { name, min_len, max_len, decl_req, type_req, fn_type_req, handler } */
{ "interrupt", 0, 0, false, true, true, c4x_handle_fntype_attribute },
{ "naked", 0, 0, false, true, true, c4x_handle_fntype_attribute },
{ "leaf_pretend", 0, 0, false, true, true, c4x_handle_fntype_attribute },
{ NULL, 0, 0, false, false, false, NULL }
};
 
/* Handle an attribute requiring a FUNCTION_TYPE;
arguments as in struct attribute_spec.handler. */
static tree
c4x_handle_fntype_attribute (tree *node, tree name,
tree args ATTRIBUTE_UNUSED,
int flags ATTRIBUTE_UNUSED,
bool *no_add_attrs)
{
if (TREE_CODE (*node) != FUNCTION_TYPE)
{
warning (OPT_Wattributes, "%qs attribute only applies to functions",
IDENTIFIER_POINTER (name));
*no_add_attrs = true;
}
 
return NULL_TREE;
}
 
 
/* !!! FIXME to emit RPTS correctly. */
 
int
c4x_rptb_rpts_p (rtx insn, rtx 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",
insn);
return 0;
}
 
/* Skip any notes. */
insn = next_nonnote_insn (insn);
 
/* This should be our first insn in the loop. */
if (! INSN_P (insn))
return 0;
 
/* Skip any notes. */
insn = next_nonnote_insn (insn);
 
if (! INSN_P (insn))
return 0;
 
if (recog_memoized (insn) != CODE_FOR_rptb_end)
return 0;
 
if (TARGET_RPTS)
return 1;
 
return (GET_CODE (op) == CONST_INT) && TARGET_RPTS_CYCLES (INTVAL (op));
}
 
 
/* Check if register r11 is used as the destination of an insn. */
 
static int
c4x_r11_set_p(rtx x)
{
rtx set;
int i, j;
const char *fmt;
 
if (x == 0)
return 0;
 
if (INSN_P (x) && GET_CODE (PATTERN (x)) == SEQUENCE)
x = XVECEXP (PATTERN (x), 0, XVECLEN (PATTERN (x), 0) - 1);
 
if (INSN_P (x) && (set = single_set (x)))
x = SET_DEST (set);
 
if (GET_CODE (x) == REG && REGNO (x) == R11_REGNO)
return 1;
 
fmt = GET_RTX_FORMAT (GET_CODE (x));
for (i = GET_RTX_LENGTH (GET_CODE (x)) - 1; i >= 0; i--)
{
if (fmt[i] == 'e')
{
if (c4x_r11_set_p (XEXP (x, i)))
return 1;
}
else if (fmt[i] == 'E')
for (j = XVECLEN (x, i) - 1; j >= 0; j--)
if (c4x_r11_set_p (XVECEXP (x, i, j)))
return 1;
}
return 0;
}
 
 
/* The c4x sometimes has a problem when the insn before the laj insn
sets the r11 register. Check for this situation. */
 
int
c4x_check_laj_p (rtx insn)
{
insn = prev_nonnote_insn (insn);
 
/* If this is the start of the function no nop is needed. */
if (insn == 0)
return 0;
 
/* If the previous insn is a code label we have to insert a nop. This
could be a jump or table jump. We can find the normal jumps by
scanning the function but this will not find table jumps. */
if (GET_CODE (insn) == CODE_LABEL)
return 1;
 
/* If the previous insn sets register r11 we have to insert a nop. */
if (c4x_r11_set_p (insn))
return 1;
 
/* No nop needed. */
return 0;
}
 
 
/* 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
 
static int
c4x_adjust_cost (rtx insn, rtx link, rtx dep_insn, int cost)
{
/* Don't worry about this until we know what registers have been
assigned. */
if (flag_schedule_insns == 0 && ! 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 ();
}
 
void
c4x_init_builtins (void)
{
tree endlink = void_list_node;
 
lang_hooks.builtin_function ("fast_ftoi",
build_function_type
(integer_type_node,
tree_cons (NULL_TREE, double_type_node,
endlink)),
C4X_BUILTIN_FIX, BUILT_IN_MD, NULL, NULL_TREE);
lang_hooks.builtin_function ("ansi_ftoi",
build_function_type
(integer_type_node,
tree_cons (NULL_TREE, double_type_node,
endlink)),
C4X_BUILTIN_FIX_ANSI, BUILT_IN_MD, NULL,
NULL_TREE);
if (TARGET_C3X)
lang_hooks.builtin_function ("fast_imult",
build_function_type
(integer_type_node,
tree_cons (NULL_TREE, integer_type_node,
tree_cons (NULL_TREE,
integer_type_node,
endlink))),
C4X_BUILTIN_MPYI, BUILT_IN_MD, NULL,
NULL_TREE);
else
{
lang_hooks.builtin_function ("toieee",
build_function_type
(double_type_node,
tree_cons (NULL_TREE, double_type_node,
endlink)),
C4X_BUILTIN_TOIEEE, BUILT_IN_MD, NULL,
NULL_TREE);
lang_hooks.builtin_function ("frieee",
build_function_type
(double_type_node,
tree_cons (NULL_TREE, double_type_node,
endlink)),
C4X_BUILTIN_FRIEEE, BUILT_IN_MD, NULL,
NULL_TREE);
lang_hooks.builtin_function ("fast_invf",
build_function_type
(double_type_node,
tree_cons (NULL_TREE, double_type_node,
endlink)),
C4X_BUILTIN_RCPF, BUILT_IN_MD, NULL,
NULL_TREE);
}
}
 
 
rtx
c4x_expand_builtin (tree exp, rtx target,
rtx subtarget ATTRIBUTE_UNUSED,
enum machine_mode mode ATTRIBUTE_UNUSED,
int ignore ATTRIBUTE_UNUSED)
{
tree fndecl = TREE_OPERAND (TREE_OPERAND (exp, 0), 0);
unsigned int fcode = DECL_FUNCTION_CODE (fndecl);
tree arglist = TREE_OPERAND (exp, 1);
tree arg0, arg1;
rtx r0, r1;
 
switch (fcode)
{
case C4X_BUILTIN_FIX:
arg0 = TREE_VALUE (arglist);
r0 = expand_expr (arg0, NULL_RTX, QFmode, 0);
if (! target || ! register_operand (target, QImode))
target = gen_reg_rtx (QImode);
emit_insn (gen_fixqfqi_clobber (target, r0));
return target;
 
case C4X_BUILTIN_FIX_ANSI:
arg0 = TREE_VALUE (arglist);
r0 = expand_expr (arg0, NULL_RTX, QFmode, 0);
if (! target || ! register_operand (target, QImode))
target = gen_reg_rtx (QImode);
emit_insn (gen_fix_truncqfqi2 (target, r0));
return target;
 
case C4X_BUILTIN_MPYI:
if (! TARGET_C3X)
break;
arg0 = TREE_VALUE (arglist);
arg1 = TREE_VALUE (TREE_CHAIN (arglist));
r0 = expand_expr (arg0, NULL_RTX, QImode, 0);
r1 = expand_expr (arg1, NULL_RTX, QImode, 0);
if (! target || ! register_operand (target, QImode))
target = gen_reg_rtx (QImode);
emit_insn (gen_mulqi3_24_clobber (target, r0, r1));
return target;
 
case C4X_BUILTIN_TOIEEE:
if (TARGET_C3X)
break;
arg0 = TREE_VALUE (arglist);
r0 = expand_expr (arg0, NULL_RTX, QFmode, 0);
if (! target || ! register_operand (target, QFmode))
target = gen_reg_rtx (QFmode);
emit_insn (gen_toieee (target, r0));
return target;
 
case C4X_BUILTIN_FRIEEE:
if (TARGET_C3X)
break;
arg0 = TREE_VALUE (arglist);
r0 = expand_expr (arg0, NULL_RTX, QFmode, 0);
if (register_operand (r0, QFmode))
{
r1 = assign_stack_local (QFmode, GET_MODE_SIZE (QFmode), 0);
emit_move_insn (r1, r0);
r0 = r1;
}
if (! target || ! register_operand (target, QFmode))
target = gen_reg_rtx (QFmode);
emit_insn (gen_frieee (target, r0));
return target;
 
case C4X_BUILTIN_RCPF:
if (TARGET_C3X)
break;
arg0 = TREE_VALUE (arglist);
r0 = expand_expr (arg0, NULL_RTX, QFmode, 0);
if (! target || ! register_operand (target, QFmode))
target = gen_reg_rtx (QFmode);
emit_insn (gen_rcpfqf_clobber (target, r0));
return target;
}
return NULL_RTX;
}
 
static void
c4x_init_libfuncs (void)
{
set_optab_libfunc (smul_optab, QImode, "__mulqi3");
set_optab_libfunc (sdiv_optab, QImode, "__divqi3");
set_optab_libfunc (udiv_optab, QImode, "__udivqi3");
set_optab_libfunc (smod_optab, QImode, "__modqi3");
set_optab_libfunc (umod_optab, QImode, "__umodqi3");
set_optab_libfunc (sdiv_optab, QFmode, "__divqf3");
set_optab_libfunc (smul_optab, HFmode, "__mulhf3");
set_optab_libfunc (sdiv_optab, HFmode, "__divhf3");
set_optab_libfunc (smul_optab, HImode, "__mulhi3");
set_optab_libfunc (sdiv_optab, HImode, "__divhi3");
set_optab_libfunc (udiv_optab, HImode, "__udivhi3");
set_optab_libfunc (smod_optab, HImode, "__modhi3");
set_optab_libfunc (umod_optab, HImode, "__umodhi3");
set_optab_libfunc (ffs_optab, QImode, "__ffs");
smulhi3_libfunc = init_one_libfunc ("__smulhi3_high");
umulhi3_libfunc = init_one_libfunc ("__umulhi3_high");
fix_truncqfhi2_libfunc = init_one_libfunc ("__fix_truncqfhi2");
fixuns_truncqfhi2_libfunc = init_one_libfunc ("__ufix_truncqfhi2");
fix_trunchfhi2_libfunc = init_one_libfunc ("__fix_trunchfhi2");
fixuns_trunchfhi2_libfunc = init_one_libfunc ("__ufix_trunchfhi2");
floathiqf2_libfunc = init_one_libfunc ("__floathiqf2");
floatunshiqf2_libfunc = init_one_libfunc ("__ufloathiqf2");
floathihf2_libfunc = init_one_libfunc ("__floathihf2");
floatunshihf2_libfunc = init_one_libfunc ("__ufloathihf2");
}
 
static void
c4x_asm_named_section (const char *name, unsigned int flags ATTRIBUTE_UNUSED,
tree decl ATTRIBUTE_UNUSED)
{
fprintf (asm_out_file, "\t.sect\t\"%s\"\n", name);
}
 
static void
c4x_globalize_label (FILE *stream, const char *name)
{
default_globalize_label (stream, name);
c4x_global_label (name);
}
#define SHIFT_CODE_P(C) \
((C) == ASHIFT || (C) == ASHIFTRT || (C) == LSHIFTRT)
#define LOGICAL_CODE_P(C) \
((C) == NOT || (C) == AND || (C) == IOR || (C) == XOR)
 
/* Compute a (partial) cost for rtx X. Return true if the complete
cost has been computed, and false if subexpressions should be
scanned. In either case, *TOTAL contains the cost result. */
 
static bool
c4x_rtx_costs (rtx x, int code, int outer_code, int *total)
{
HOST_WIDE_INT val;
 
switch (code)
{
/* 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. */
 
case CONST_INT:
val = INTVAL (x);
if (c4x_J_constant (x))
*total = 0;
else if (! TARGET_C3X
&& outer_code == AND
&& (val == 255 || val == 65535))
*total = 0;
else if (! TARGET_C3X
&& (outer_code == ASHIFTRT || outer_code == LSHIFTRT)
&& (val == 16 || val == 24))
*total = 0;
else if (TARGET_C3X && SHIFT_CODE_P (outer_code))
*total = 3;
else if (LOGICAL_CODE_P (outer_code)
? c4x_L_constant (x) : c4x_I_constant (x))
*total = 2;
else
*total = 4;
return true;
 
case CONST:
case LABEL_REF:
case SYMBOL_REF:
*total = 4;
return true;
 
case CONST_DOUBLE:
if (c4x_H_constant (x))
*total = 2;
else if (GET_MODE (x) == QFmode)
*total = 4;
else
*total = 8;
return true;
 
/* ??? Note that we return true, rather than false so that rtx_cost
doesn't include the constant costs. Otherwise expand_mult will
think that it is cheaper to synthesize 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. */
case PLUS:
case MINUS:
case AND:
case IOR:
case XOR:
case ASHIFT:
case ASHIFTRT:
case LSHIFTRT:
*total = COSTS_N_INSNS (1);
return true;
 
case MULT:
*total = COSTS_N_INSNS (GET_MODE_CLASS (GET_MODE (x)) == MODE_FLOAT
|| TARGET_MPYI ? 1 : 14);
return true;
 
case DIV:
case UDIV:
case MOD:
case UMOD:
*total = COSTS_N_INSNS (GET_MODE_CLASS (GET_MODE (x)) == MODE_FLOAT
? 15 : 50);
return true;
 
default:
return false;
}
}
/* Worker function for TARGET_ASM_EXTERNAL_LIBCALL. */
 
static void
c4x_external_libcall (rtx fun)
{
/* This is only needed to keep asm30 happy for ___divqf3 etc. */
c4x_external_ref (XSTR (fun, 0));
}
 
/* Worker function for TARGET_STRUCT_VALUE_RTX. */
 
static rtx
c4x_struct_value_rtx (tree fntype ATTRIBUTE_UNUSED,
int incoming ATTRIBUTE_UNUSED)
{
return gen_rtx_REG (Pmode, AR0_REGNO);
}
/c4x.opt
0,0 → 1,139
; Options for the TMS320C[34]x port of the compiler.
 
; Copyright (C) 2005, 2007 Free Software Foundation, Inc.
;
; This file is part of GCC.
;
; GCC is free software; you can redistribute it and/or modify it under
; the terms of the GNU General Public License as published by the Free
; Software Foundation; either version 3, or (at your option) any later
; version.
;
; GCC is distributed in the hope that it will be useful, but WITHOUT ANY
; WARRANTY; without even the implied warranty of MERCHANTABILITY or
; FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
; for more details.
;
; You should have received a copy of the GNU General Public License
; along with GCC; see the file COPYING3. If not see
; <http://www.gnu.org/licenses/>.
 
m30
Target RejectNegative
Generate code for C30 CPU
 
m31
Target RejectNegative
Generate code for C31 CPU
 
m32
Target RejectNegative
Generate code for C32 CPU
 
m33
Target RejectNegative
Generate code for C33 CPU
 
m40
Target RejectNegative
Generate code for C40 CPU
 
m44
Target RejectNegative
Generate code for C44 CPU
 
maliases
Target Report Mask(ALIASES)
Assume that pointers may be aliased
 
mbig
Target RejectNegative Report InverseMask(SMALL)
Big memory model
 
mbk
Target Report Mask(BK)
Use the BK register as a general purpose register
 
mcpu=
Target RejectNegative Joined
-mcpu=CPU Generate code for CPU
 
mdb
Target Report Mask(DB)
Enable use of DB instruction
 
mdebug
Target Report Mask(DEBUG)
Enable debugging
 
mdevel
Target Report Mask(DEVEL)
Enable new features under development
 
mfast-fix
Target Report Mask(FAST_FIX)
Use fast but approximate float to integer conversion
 
mforce
Target Report Mask(FORCE)
Force RTL generation to emit valid 3 operand insns
 
mhoist
Target Report Mask(HOIST)
Force constants into registers to improve hoisting
 
misr-dp-reload
Target Mask(PARANOID) MaskExists
Save DP across ISR in small memory model
 
mloop-unsigned
Target Report Mask(LOOP_UNSIGNED)
Allow unsigned iteration counts for RPTB/DB
 
mmemparm
Target RejectNegative Report Mask(MEMPARM)
Pass arguments on the stack
 
mmpyi
Target Report Mask(MPYI)
Use MPYI instruction for C3x
 
mparallel-insns
Target Report Mask(PARALLEL)
Enable parallel instructions
 
mparallel-mpy
Target Report Mask(PARALLEL_MPY)
Enable MPY||ADD and MPY||SUB instructions
 
mparanoid
Target Report Mask(PARANOID)
Save DP across ISR in small memory model
 
mpreserve-float
Target Report Mask(PRESERVE_FLOAT)
Preserve all 40 bits of FP reg across call
 
mregparm
Target RejectNegative Report InverseMask(MEMPARM)
Pass arguments in registers
 
mrptb
Target Report Mask(RPTB)
Enable use of RTPB instruction
 
mrpts
Target Report Mask(RPTS)
Enable use of RTPS instruction
 
mrpts=
Target RejectNegative Joined UInteger Var(c4x_rpts_cycles)
-mrpts=N Set the maximum number of iterations for RPTS to N
 
msmall
Target RejectNegative Report Mask(SMALL)
Small memory model
 
mti
Target Report Mask(TI)
Emit code compatible with TI tools
/t-c4x
0,0 → 1,20
LIB1ASMSRC = c4x/libgcc.S
LIB1ASMFUNCS = _divsf3 _divsi3 _udivsi3 _umodsi3 _modsi3 _mulsi3 \
_muldf3 _divdf3 _unsfltconst _unsfltcompare \
_muldi3 _umuldi3_high _smuldi3_high _divdi3 _moddi3 _umoddi3 _udivdi3 \
_fix_truncsfdi2 _ufix_truncsfdi2 _floatdisf2 _ufloatdisf2 \
_floatdidf2 _ufloatdidf2 _fix_truncdfdi2 _ufix_truncdfdi2 _ffs
 
TARGET_LIBGCC2_CFLAGS = -Dexit=unused_exit
 
c4x-c.o: $(srcdir)/config/c4x/c4x-c.c $(CONFIG_H) $(SYSTEM_H) coretypes.h \
$(TM_H) $(TREE_H) toplev.h $(CPPLIB_H) c-pragma.h $(TM_P_H)
$(CC) -c $(ALL_CFLAGS) $(ALL_CPPFLAGS) $(INCLUDES) $(srcdir)/config/c4x/c4x-c.c
 
MULTILIB_OPTIONS = m30 msmall mmemparm
MULTILIB_DIRNAMES = c3x small mem
MULTILIB_MATCHES = m30=mcpu?30 m30=mcpu?31 m30=mcpu?32 m30=mcpu?33 m30=m31 m30=m32 m30=m33
MULTILIB_EXCEPTIONS =
MULTILIB_EXTRA_OPTS =
LIBGCC = stmp-multilib
INSTALL_LIBGCC = install-multilib
/c4x-modes.def
0,0 → 1,108
/* Definitions of target machine for GNU compiler. TMS320C[34]x
Copyright (C) 2002, 2004, 2007 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 GCC.
 
GCC is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3, or (at your option)
any later version.
 
GCC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
 
You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING3. If not see
<http://www.gnu.org/licenses/>. */
 
/* C4x wants 1- and 2-word float modes, in its own peculiar format.
FIXME: Give this port a way to get rid of SFmode, DFmode, and all
the other modes it doesn't use. */
FLOAT_MODE (QF, 1, c4x_single_format);
FLOAT_MODE (HF, 2, c4x_extended_format);
RESET_FLOAT_FORMAT (SF, 0); /* not used */
RESET_FLOAT_FORMAT (DF, 0); /* not used */
 
/* 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 optimizing 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 occurred (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 occurred.
 
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 cannot 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. */
 
CC_MODE (CC_NOOV);
/rtems.h
0,0 → 1,28
/* Definitions of RTEMS executing on an TMS320C[34]x using coff
Copyright (C) 1996, 1997, 1999, 2002, 2007 Free Software Foundation, Inc.
Contributed by Joel Sherrill (joel@OARcorp.com).
 
This file is part of GCC.
 
GCC is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3, or (at your option)
any later version.
 
GCC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
 
You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING3. If not see
<http://www.gnu.org/licenses/>. */
 
/* Specify predefined symbols in preprocessor. */
 
#define TARGET_OS_CPP_BUILTINS() \
do { \
builtin_define ("__rtems__"); \
builtin_define ("__USE_INIT_FINI__"); \
builtin_assert ("system=rtems"); \
} while (0)
/predicates.md
0,0 → 1,403
;; Predicate definitions for TMS320C[34]x.
;; Copyright (C) 2005, 2007 Free Software Foundation, Inc.
;;
;; This file is part of GCC.
;;
;; GCC is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation; either version 3, or (at your option)
;; any later version.
;;
;; GCC is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;;
;; You should have received a copy of the GNU General Public License
;; along with GCC; see the file COPYING3. If not see
;; <http://www.gnu.org/licenses/>.
 
;; Nonzero if OP is a floating point value with value 0.0.
 
(define_predicate "fp_zero_operand"
(match_code "const_double")
{
REAL_VALUE_TYPE r;
 
if (GET_CODE (op) != CONST_DOUBLE)
return 0;
REAL_VALUE_FROM_CONST_DOUBLE (r, op);
return REAL_VALUES_EQUAL (r, dconst0);
})
 
;; TODO: Add a comment here.
 
(define_predicate "const_operand"
(match_code "const_int,const_double")
{
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;
}
})
 
;; TODO: Add a comment here.
 
(define_predicate "stik_const_operand"
(match_code "const_int")
{
return c4x_K_constant (op);
})
 
;; TODO: Add a comment here.
 
(define_predicate "not_const_operand"
(match_code "const_int")
{
return c4x_N_constant (op);
})
 
;; TODO: Add a comment here.
 
(define_predicate "reg_operand"
(match_code "reg,subreg")
{
if (GET_CODE (op) == SUBREG
&& GET_MODE (op) == QFmode)
return 0;
return register_operand (op, mode);
})
 
;; TODO: Add a comment here.
 
(define_predicate "reg_or_const_operand"
(match_code "reg,subreg,const_int,const_double")
{
return reg_operand (op, mode) || const_operand (op, mode);
})
 
;; Extended precision register R0-R1.
 
(define_predicate "r0r1_reg_operand"
(match_code "reg,subreg")
{
if (! reg_operand (op, mode))
return 0;
if (GET_CODE (op) == SUBREG)
op = SUBREG_REG (op);
return REG_P (op) && IS_R0R1_OR_PSEUDO_REG (op);
})
 
;; Extended precision register R2-R3.
 
(define_predicate "r2r3_reg_operand"
(match_code "reg,subreg")
{
if (! reg_operand (op, mode))
return 0;
if (GET_CODE (op) == SUBREG)
op = SUBREG_REG (op);
return REG_P (op) && IS_R2R3_OR_PSEUDO_REG (op);
})
 
;; Low extended precision register R0-R7.
 
(define_predicate "ext_low_reg_operand"
(match_code "reg,subreg")
{
if (! reg_operand (op, mode))
return 0;
if (GET_CODE (op) == SUBREG)
op = SUBREG_REG (op);
return REG_P (op) && IS_EXT_LOW_OR_PSEUDO_REG (op);
})
 
;; Extended precision register.
 
(define_predicate "ext_reg_operand"
(match_code "reg,subreg")
{
if (! reg_operand (op, mode))
return 0;
if (GET_CODE (op) == SUBREG)
op = SUBREG_REG (op);
if (! REG_P (op))
return 0;
return IS_EXT_OR_PSEUDO_REG (op);
})
 
;; Standard precision register.
 
(define_predicate "std_reg_operand"
(match_code "reg,subreg")
{
if (! reg_operand (op, mode))
return 0;
if (GET_CODE (op) == SUBREG)
op = SUBREG_REG (op);
return REG_P (op) && IS_STD_OR_PSEUDO_REG (op);
})
 
;; Standard precision or normal register.
 
(define_predicate "std_or_reg_operand"
(match_code "reg,subreg")
{
if (reload_in_progress)
return std_reg_operand (op, mode);
return reg_operand (op, mode);
})
 
;; Address register.
 
(define_predicate "addr_reg_operand"
(match_code "reg,subreg")
{
if (! reg_operand (op, mode))
return 0;
return c4x_a_register (op);
})
 
;; Index register.
 
(define_predicate "index_reg_operand"
(match_code "reg,subreg")
{
if (! reg_operand (op, mode))
return 0;
if (GET_CODE (op) == SUBREG)
op = SUBREG_REG (op);
return c4x_x_register (op);
})
 
;; DP register.
 
(define_predicate "dp_reg_operand"
(match_code "reg")
{
return REG_P (op) && IS_DP_OR_PSEUDO_REG (op);
})
 
;; SP register.
 
(define_predicate "sp_reg_operand"
(match_code "reg")
{
return REG_P (op) && IS_SP_OR_PSEUDO_REG (op);
})
 
;; ST register.
 
(define_predicate "st_reg_operand"
(match_code "reg")
{
return REG_P (op) && IS_ST_OR_PSEUDO_REG (op);
})
 
;; RC register.
 
(define_predicate "rc_reg_operand"
(match_code "reg")
{
return REG_P (op) && IS_RC_OR_PSEUDO_REG (op);
})
 
;; TODO: Add a comment here.
 
(define_predicate "call_address_operand"
(match_code "reg,symbol_ref,label_ref,const")
{
return (REG_P (op) || symbolic_address_operand (op, mode));
})
 
;; Check dst operand of a move instruction.
 
(define_predicate "dst_operand"
(match_code "subreg,reg,mem")
{
if (GET_CODE (op) == SUBREG
&& mixed_subreg_operand (op, mode))
return 0;
 
if (REG_P (op))
return reg_operand (op, mode);
 
return nonimmediate_operand (op, mode);
})
 
;; Check src operand of two operand arithmetic instructions.
 
(define_predicate "src_operand"
(match_code "subreg,reg,mem,const_int,const_double")
{
if (GET_CODE (op) == SUBREG
&& mixed_subreg_operand (op, mode))
return 0;
 
if (REG_P (op))
return reg_operand (op, mode);
 
if (mode == VOIDmode)
mode = GET_MODE (op);
 
if (GET_CODE (op) == CONST_INT)
return (mode == QImode || mode == Pmode || mode == HImode)
&& c4x_I_constant (op);
 
/* We don't like CONST_DOUBLE integers. */
if (GET_CODE (op) == CONST_DOUBLE)
return c4x_H_constant (op);
 
/* Disallow symbolic addresses. Only the predicate
symbolic_address_operand will match these. */
if (GET_CODE (op) == SYMBOL_REF
|| GET_CODE (op) == LABEL_REF
|| GET_CODE (op) == CONST)
return 0;
 
/* If TARGET_LOAD_DIRECT_MEMS is nonzero, disallow direct memory
access to symbolic addresses. These operands will get forced
into a register and the movqi expander will generate a
HIGH/LO_SUM pair if TARGET_EXPOSE_LDP is nonzero. */
if (GET_CODE (op) == MEM
&& ((GET_CODE (XEXP (op, 0)) == SYMBOL_REF
|| GET_CODE (XEXP (op, 0)) == LABEL_REF
|| GET_CODE (XEXP (op, 0)) == CONST)))
return !TARGET_EXPOSE_LDP &&
! TARGET_LOAD_DIRECT_MEMS && GET_MODE (op) == mode;
 
return general_operand (op, mode);
})
 
;; TODO: Add a comment here.
 
(define_predicate "src_hi_operand"
(match_code "subreg,reg,mem,const_double")
{
if (c4x_O_constant (op))
return 1;
return src_operand (op, mode);
})
 
;; Check src operand of two operand logical instructions.
 
(define_predicate "lsrc_operand"
(match_code "subreg,reg,mem,const_int,const_double")
{
if (mode == VOIDmode)
mode = GET_MODE (op);
 
if (mode != QImode && mode != Pmode)
fatal_insn ("mode not QImode", op);
 
if (GET_CODE (op) == CONST_INT)
return c4x_L_constant (op) || c4x_J_constant (op);
 
return src_operand (op, mode);
})
 
;; Check src operand of two operand tricky instructions.
 
(define_predicate "tsrc_operand"
(match_code "subreg,reg,mem,const_int,const_double")
{
if (mode == VOIDmode)
mode = GET_MODE (op);
 
if (mode != QImode && mode != Pmode)
fatal_insn ("mode not QImode", op);
 
if (GET_CODE (op) == CONST_INT)
return c4x_L_constant (op) || c4x_N_constant (op) || c4x_J_constant (op);
 
return src_operand (op, mode);
})
 
;; Check src operand of two operand non immediate instructions.
 
(define_predicate "nonimmediate_src_operand"
(match_code "subreg,reg,mem")
{
if (GET_CODE (op) == CONST_INT || GET_CODE (op) == CONST_DOUBLE)
return 0;
 
return src_operand (op, mode);
})
 
;; Check logical src operand of two operand non immediate instructions.
 
(define_predicate "nonimmediate_lsrc_operand"
(match_code "subreg,reg,mem")
{
if (GET_CODE (op) == CONST_INT || GET_CODE (op) == CONST_DOUBLE)
return 0;
 
return lsrc_operand (op, mode);
})
 
;; Match any operand.
 
(define_predicate "any_operand"
(match_code "subreg,reg,mem,const_int,const_double")
{
return 1;
})
 
;; Check for indirect operands allowable in parallel instruction.
 
(define_predicate "par_ind_operand"
(match_code "mem")
{
if (mode != VOIDmode && mode != GET_MODE (op))
return 0;
 
return c4x_S_indirect (op);
})
 
;; Check for operands allowable in parallel instruction.
 
(define_predicate "parallel_operand"
(match_code "subreg,reg,mem")
{
return ext_low_reg_operand (op, mode) || par_ind_operand (op, mode);
})
 
;; Symbolic address operand.
 
(define_predicate "symbolic_address_operand"
(match_code "symbol_ref,label_ref,const")
{
switch (GET_CODE (op))
{
case CONST:
case SYMBOL_REF:
case LABEL_REF:
return 1;
default:
return 0;
}
})
/c4x.h
0,0 → 1,1654
/* Definitions of target machine for GNU compiler. TMS320C[34]x
Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
2003, 2004, 2005, 2007 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 GCC.
 
GCC is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3, or (at your option)
any later version.
 
GCC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
 
You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING3. If not see
<http://www.gnu.org/licenses/>. */
 
/* RUN-TIME TARGET SPECIFICATION. */
 
#define C4x 1
 
#define TARGET_CPU_CPP_BUILTINS() \
do \
{ \
extern int flag_inline_trees; \
if (!TARGET_SMALL) \
builtin_define ("_BIGMODEL"); \
if (!TARGET_MEMPARM) \
builtin_define ("_REGPARM"); \
if (flag_inline_functions) \
builtin_define ("_INLINE"); \
if (TARGET_C3X) \
{ \
builtin_define ("_TMS320C3x"); \
builtin_define ("_C3x"); \
if (TARGET_C30) \
{ \
builtin_define ("_TMS320C30"); \
builtin_define ("_C30"); \
} \
else if (TARGET_C31) \
{ \
builtin_define ("_TMS320C31"); \
builtin_define ("_C31"); \
} \
else if (TARGET_C32) \
{ \
builtin_define ("_TMS320C32"); \
builtin_define ("_C32"); \
} \
else if (TARGET_C33) \
{ \
builtin_define ("_TMS320C33"); \
builtin_define ("_C33"); \
} \
} \
else \
{ \
builtin_define ("_TMS320C4x"); \
builtin_define ("_C4x"); \
if (TARGET_C40) \
{ \
builtin_define ("_TMS320C40"); \
builtin_define ("_C40"); \
} \
else if (TARGET_C44) \
{ \
builtin_define ("_TMS320C44"); \
builtin_define ("_C44"); \
} \
} \
} \
while (0)
 
/* Define assembler options. */
 
#define ASM_SPEC "\
%{!mcpu=30:%{!mcpu=31:%{!mcpu=32:%{!mcpu=33:%{!mcpu=40:%{!mcpu=44:\
%{!m30:%{!m31:%{!m32:%{!m33:%{!m40:%{!m44:-m40}}}}}}}}}}}} \
%{mcpu=30} \
%{mcpu=31} \
%{mcpu=32} \
%{mcpu=33} \
%{mcpu=40} \
%{mcpu=44} \
%{m30} \
%{m31} \
%{m32} \
%{m33} \
%{m40} \
%{m44} \
%{mmemparm} %{mregparm} %{!mmemparm:%{!mregparm:-mregparm}} \
%{mbig} %{msmall} %{!msmall:%{!mbig:-mbig}}"
 
/* Define linker options. */
 
#define LINK_SPEC "\
%{m30:--architecture c3x} \
%{m31:--architecture c3x} \
%{m32:--architecture c3x} \
%{m33:--architecture c3x} \
%{mcpu=30:--architecture c3x} \
%{mcpu=31:--architecture c3x} \
%{mcpu=32:--architecture c3x} \
%{mcpu=33:--architecture c3x}"
 
/* Specify the end file to link with. */
 
#define ENDFILE_SPEC ""
 
/* 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 c4x_cpu_version; /* Cpu version C30/31/32/33/40/44. */
 
#define TARGET_INLINE (! optimize_size) /* Inline MPYI. */
#define TARGET_SMALL_REG_CLASS 0
 
#define TARGET_C3X (c4x_cpu_version >= 30 \
&& c4x_cpu_version <= 39)
 
#define TARGET_C30 (c4x_cpu_version == 30)
#define TARGET_C31 (c4x_cpu_version == 31)
#define TARGET_C32 (c4x_cpu_version == 32)
#define TARGET_C33 (c4x_cpu_version == 33)
#define TARGET_C40 (c4x_cpu_version == 40)
#define TARGET_C44 (c4x_cpu_version == 44)
 
/* Nonzero to use load_immed_addr pattern rather than forcing memory
addresses into memory. */
#define TARGET_LOAD_ADDRESS (1 || (! TARGET_C3X && ! TARGET_SMALL))
 
/* Nonzero to convert direct memory references into HIGH/LO_SUM pairs
during RTL generation. */
#define TARGET_EXPOSE_LDP 0
 
/* Nonzero to force loading of direct memory references into a register. */
#define TARGET_LOAD_DIRECT_MEMS 0
 
/* -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)
 
/* 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. */
 
#define OVERRIDE_OPTIONS c4x_override_options ()
 
/* Define this to change the optimizations performed by default. */
 
#define OPTIMIZATION_OPTIONS(LEVEL,SIZE) c4x_optimization_options(LEVEL, SIZE)
 
/* Run Time Target Specification. */
 
#define TARGET_VERSION fprintf (stderr, " (TMS320C[34]x, TI syntax)");
 
/* Storage Layout. */
 
#define BITS_BIG_ENDIAN 0
#define BYTES_BIG_ENDIAN 0
#define WORDS_BIG_ENDIAN 0
 
/* Technically, we are little endian, but we put the floats out as
whole longs and this makes GCC put them out in the right order. */
 
#define FLOAT_WORDS_BIG_ENDIAN 1
 
/* Note the ANSI C standard requires sizeof(char) = 1. On the C[34]x
all integral and floating point data types are stored in memory as
32-bits (floating point types can be stored as 40-bits in the
extended precision registers), so sizeof(char) = sizeof(short) =
sizeof(int) = sizeof(long) = sizeof(float) = sizeof(double) = 1. */
 
#define BITS_PER_UNIT 32
#define UNITS_PER_WORD 1
#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. */
 
/* If a structure has a floating point field then force structure
to have BLKMODE, unless it is the only field. */
#define MEMBER_TYPE_FORCES_BLK(FIELD, MODE) \
(TREE_CODE (TREE_TYPE (FIELD)) == REAL_TYPE && (MODE) == VOIDmode)
 
/* Number of bits in the high and low parts of a two stage
load of an immediate constant. */
#define BITS_PER_HIGH 16
#define BITS_PER_LO_SUM 16
 
/* 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_REGNO(r) \
((unsigned int)((r) - R0_REGNO) <= (R1_REGNO - R0_REGNO))
#define IS_R2R3_REGNO(r) \
((unsigned int)((r) - R2_REGNO) <= (R3_REGNO - R2_REGNO))
#define IS_EXT_LOW_REGNO(r) \
((unsigned int)((r) - R0_REGNO) <= (R7_REGNO - R0_REGNO))
 
/* Extended precision registers (high set). */
 
#define IS_EXT_HIGH_REGNO(r) \
(! TARGET_C3X \
&& ((unsigned int) ((r) - R8_REGNO) <= (R11_REGNO - R8_REGNO)))
 
/* Address registers. */
 
#define IS_AUX_REGNO(r) \
((unsigned int)((r) - AR0_REGNO) <= (AR7_REGNO - AR0_REGNO))
#define IS_ADDR_REGNO(r) IS_AUX_REGNO(r)
#define IS_DP_REGNO(r) ((r) == DP_REGNO)
#define IS_INDEX_REGNO(r) (((r) == IR0_REGNO) || ((r) == IR1_REGNO))
#define IS_SP_REGNO(r) ((r) == SP_REGNO)
#define IS_BK_REGNO(r) (TARGET_BK && (r) == BK_REGNO)
 
/* Misc registers. */
 
#define IS_ST_REGNO(r) ((r) == ST_REGNO)
#define IS_RC_REGNO(r) ((r) == RC_REGNO)
#define IS_REPEAT_REGNO(r) (((r) >= RS_REGNO) && ((r) <= RC_REGNO))
 
/* Composite register sets. */
 
#define IS_ADDR_OR_INDEX_REGNO(r) (IS_ADDR_REGNO(r) || IS_INDEX_REGNO(r))
#define IS_EXT_REGNO(r) (IS_EXT_LOW_REGNO(r) || IS_EXT_HIGH_REGNO(r))
#define IS_STD_REGNO(r) (IS_ADDR_OR_INDEX_REGNO(r) \
|| IS_REPEAT_REGNO(r) \
|| IS_SP_REGNO(r) \
|| IS_BK_REGNO(r))
#define IS_INT_REGNO(r) (IS_EXT_REGNO(r) || IS_STD_REGNO(r))
#define IS_GROUP1_REGNO(r) (IS_ADDR_OR_INDEX_REGNO(r) || IS_BK_REGNO(r))
#define IS_INT_CALL_SAVED_REGNO(r) (((r) == R4_REGNO) || ((r) == R5_REGNO) \
|| ((r) == R8_REGNO))
#define IS_FLOAT_CALL_SAVED_REGNO(r) (((r) == R6_REGNO) || ((r) == R7_REGNO))
 
#define IS_PSEUDO_REGNO(r) ((r) >= FIRST_PSEUDO_REGISTER)
#define IS_R0R1_OR_PSEUDO_REGNO(r) (IS_R0R1_REGNO(r) || IS_PSEUDO_REGNO(r))
#define IS_R2R3_OR_PSEUDO_REGNO(r) (IS_R2R3_REGNO(r) || IS_PSEUDO_REGNO(r))
#define IS_EXT_OR_PSEUDO_REGNO(r) (IS_EXT_REGNO(r) || IS_PSEUDO_REGNO(r))
#define IS_STD_OR_PSEUDO_REGNO(r) (IS_STD_REGNO(r) || IS_PSEUDO_REGNO(r))
#define IS_INT_OR_PSEUDO_REGNO(r) (IS_INT_REGNO(r) || IS_PSEUDO_REGNO(r))
#define IS_ADDR_OR_PSEUDO_REGNO(r) (IS_ADDR_REGNO(r) || IS_PSEUDO_REGNO(r))
#define IS_INDEX_OR_PSEUDO_REGNO(r) (IS_INDEX_REGNO(r) || IS_PSEUDO_REGNO(r))
#define IS_EXT_LOW_OR_PSEUDO_REGNO(r) (IS_EXT_LOW_REGNO(r) \
|| IS_PSEUDO_REGNO(r))
#define IS_DP_OR_PSEUDO_REGNO(r) (IS_DP_REGNO(r) || IS_PSEUDO_REGNO(r))
#define IS_SP_OR_PSEUDO_REGNO(r) (IS_SP_REGNO(r) || IS_PSEUDO_REGNO(r))
#define IS_ST_OR_PSEUDO_REGNO(r) (IS_ST_REGNO(r) || IS_PSEUDO_REGNO(r))
#define IS_RC_OR_PSEUDO_REGNO(r) (IS_RC_REGNO(r) || IS_PSEUDO_REGNO(r))
 
#define IS_PSEUDO_REG(op) (IS_PSEUDO_REGNO(REGNO(op)))
#define IS_ADDR_REG(op) (IS_ADDR_REGNO(REGNO(op)))
#define IS_INDEX_REG(op) (IS_INDEX_REGNO(REGNO(op)))
#define IS_GROUP1_REG(r) (IS_GROUP1_REGNO(REGNO(op)))
#define IS_SP_REG(op) (IS_SP_REGNO(REGNO(op)))
#define IS_STD_REG(op) (IS_STD_REGNO(REGNO(op)))
#define IS_EXT_REG(op) (IS_EXT_REGNO(REGNO(op)))
 
#define IS_R0R1_OR_PSEUDO_REG(op) (IS_R0R1_OR_PSEUDO_REGNO(REGNO(op)))
#define IS_R2R3_OR_PSEUDO_REG(op) (IS_R2R3_OR_PSEUDO_REGNO(REGNO(op)))
#define IS_EXT_OR_PSEUDO_REG(op) (IS_EXT_OR_PSEUDO_REGNO(REGNO(op)))
#define IS_STD_OR_PSEUDO_REG(op) (IS_STD_OR_PSEUDO_REGNO(REGNO(op)))
#define IS_EXT_LOW_OR_PSEUDO_REG(op) (IS_EXT_LOW_OR_PSEUDO_REGNO(REGNO(op)))
#define IS_INT_OR_PSEUDO_REG(op) (IS_INT_OR_PSEUDO_REGNO(REGNO(op)))
 
#define IS_ADDR_OR_PSEUDO_REG(op) (IS_ADDR_OR_PSEUDO_REGNO(REGNO(op)))
#define IS_INDEX_OR_PSEUDO_REG(op) (IS_INDEX_OR_PSEUDO_REGNO(REGNO(op)))
#define IS_DP_OR_PSEUDO_REG(op) (IS_DP_OR_PSEUDO_REGNO(REGNO(op)))
#define IS_SP_OR_PSEUDO_REG(op) (IS_SP_OR_PSEUDO_REGNO(REGNO(op)))
#define IS_ST_OR_PSEUDO_REG(op) (IS_ST_OR_PSEUDO_REGNO(REGNO(op)))
#define IS_RC_OR_PSEUDO_REG(op) (IS_RC_OR_PSEUDO_REGNO(REGNO(op)))
 
/* 1 for registers that have pervasive standard uses
and are not available for the register allocator. */
 
#define FIXED_REGISTERS \
{ \
/* R0 R1 R2 R3 R4 R5 R6 R7 AR0 AR1 AR2 AR3 AR4 AR5 AR6 AR7. */ \
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \
/* DP IR0 IR1 BK SP ST DIE IIE IIF RS RE RC R8 R9 R10 R11. */ \
1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0 \
}
 
/* 1 for registers not available across function calls.
These must include the FIXED_REGISTERS and also any
registers that can be used without being saved.
The latter must include the registers where values are returned
and the register where structure-value addresses are passed.
Aside from that, you can include as many other registers as you like.
Note that the extended precision registers are only saved in some
modes. The macro HARD_REGNO_CALL_CLOBBERED specifies which modes
get clobbered for a given regno. */
 
#define CALL_USED_REGISTERS \
{ \
/* R0 R1 R2 R3 R4 R5 R6 R7 AR0 AR1 AR2 AR3 AR4 AR5 AR6 AR7. */ \
1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, \
/* DP IR0 IR1 BK SP ST DIE IIE IIF RS RE RC R8 R9 R10 R11. */ \
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1 \
}
 
/* Macro to conditionally modify fixed_regs/call_used_regs. */
 
#define CONDITIONAL_REGISTER_USAGE \
{ \
if (! TARGET_BK) \
{ \
fixed_regs[BK_REGNO] = 1; \
call_used_regs[BK_REGNO] = 1; \
c4x_regclass_map[BK_REGNO] = NO_REGS; \
} \
if (TARGET_C3X) \
{ \
int i; \
\
reg_names[DIE_REGNO] = "ie"; /* Clobber die. */ \
reg_names[IF_REGNO] = "if"; /* Clobber iie. */ \
reg_names[IOF_REGNO] = "iof"; /* Clobber iif. */ \
\
for (i = R8_REGNO; i <= R11_REGNO; i++) \
{ \
fixed_regs[i] = call_used_regs[i] = 1; \
c4x_regclass_map[i] = NO_REGS; \
} \
} \
if (TARGET_PRESERVE_FLOAT) \
{ \
c4x_caller_save_map[R6_REGNO] = HFmode; \
c4x_caller_save_map[R7_REGNO] = HFmode; \
} \
}
 
/* Order of Allocation of Registers. */
 
/* List the order in which to allocate registers. Each register must be
listed once, even those in FIXED_REGISTERS.
 
First allocate registers that don't need preservation across calls,
except index and address registers. Then allocate data registers
that require preservation across calls (even though this invokes an
extra overhead of having to save/restore these registers). Next
allocate the address and index registers, since using these
registers for arithmetic can cause pipeline stalls. Finally
allocated the fixed registers which won't be allocated anyhow. */
 
#define REG_ALLOC_ORDER \
{R0_REGNO, R1_REGNO, R2_REGNO, R3_REGNO, \
R9_REGNO, R10_REGNO, R11_REGNO, \
RS_REGNO, RE_REGNO, RC_REGNO, BK_REGNO, \
R4_REGNO, R5_REGNO, R6_REGNO, R7_REGNO, R8_REGNO, \
AR0_REGNO, AR1_REGNO, AR2_REGNO, AR3_REGNO, \
AR4_REGNO, AR5_REGNO, AR6_REGNO, AR7_REGNO, \
IR0_REGNO, IR1_REGNO, \
SP_REGNO, DP_REGNO, ST_REGNO, IE_REGNO, IF_REGNO, IOF_REGNO}
 
/* A C expression that is nonzero if hard register number REGNO2 can be
considered for use as a rename register for REGNO1 */
 
#define HARD_REGNO_RENAME_OK(REGNO1,REGNO2) \
c4x_hard_regno_rename_ok((REGNO1), (REGNO2))
 
/* 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. HFmode is 40-bits and thus fits within a single
extended precision register. Similarly, HCmode fits within two
extended precision registers. */
 
#define HARD_REGNO_NREGS(REGNO, MODE) \
(((MODE) == CCmode || (MODE) == CC_NOOVmode) ? 1 : \
((MODE) == HFmode) ? 1 : \
((MODE) == HCmode) ? 2 : \
((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) \
((IS_FLOAT_CALL_SAVED_REGNO (REGNO) && ! ((MODE) == QFmode)) \
|| (IS_INT_CALL_SAVED_REGNO (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, MODE) (c4x_caller_save_map[REGNO])
 
#define HARD_REGNO_MODE_OK(REGNO, MODE) c4x_hard_regno_mode_ok(REGNO, MODE)
 
/* A C expression that is nonzero if it is desirable to choose
register allocation so as to avoid move instructions between a
value of mode MODE1 and a value of mode MODE2.
 
Value is 1 if it is a good idea to tie two pseudo registers
when one has mode MODE1 and one has mode MODE2.
If HARD_REGNO_MODE_OK could produce different values for MODE1 and MODE2,
for any hard reg, then this must be 0 for correct output. */
 
#define MODES_TIEABLE_P(MODE1, MODE2) 0
 
 
/* Define the classes of registers for register constraints in the
machine description. Also define ranges of constants.
 
One of the classes must always be named ALL_REGS and include all hard regs.
If there is more than one class, another class must be named NO_REGS
and contain no registers.
 
The name GENERAL_REGS must be the name of a class (or an alias for
another name such as ALL_REGS). This is the class of registers
that is allowed by "g" or "r" in a register constraint.
Also, registers outside this class are allocated only when
instructions express preferences for them.
 
The classes must be numbered in nondecreasing order; that is,
a larger-numbered class must never be contained completely
in a smaller-numbered class.
 
For any two classes, it is very desirable that there be another
class that represents their union. */
enum reg_class
{
NO_REGS,
R0R1_REGS, /* 't'. */
R2R3_REGS, /* 'u'. */
EXT_LOW_REGS, /* 'q'. */
EXT_REGS, /* 'f'. */
ADDR_REGS, /* 'a'. */
INDEX_REGS, /* 'x'. */
BK_REG, /* 'k'. */
SP_REG, /* 'b'. */
RC_REG, /* 'v'. */
COUNTER_REGS, /* */
INT_REGS, /* 'c'. */
GENERAL_REGS, /* 'r'. */
DP_REG, /* 'z'. */
ST_REG, /* 'y'. */
ALL_REGS,
LIM_REG_CLASSES
};
 
#define N_REG_CLASSES (int) LIM_REG_CLASSES
 
#define REG_CLASS_NAMES \
{ \
"NO_REGS", \
"R0R1_REGS", \
"R2R3_REGS", \
"EXT_LOW_REGS", \
"EXT_REGS", \
"ADDR_REGS", \
"INDEX_REGS", \
"BK_REG", \
"SP_REG", \
"RC_REG", \
"COUNTER_REGS", \
"INT_REGS", \
"GENERAL_REGS", \
"DP_REG", \
"ST_REG", \
"ALL_REGS" \
}
 
/* Define which registers fit in which classes.
This is an initializer for a vector of HARD_REG_SET
of length N_REG_CLASSES. RC is not included in GENERAL_REGS
since the register allocator will often choose a general register
in preference to RC for the decrement_and_branch_on_count pattern. */
 
#define REG_CLASS_CONTENTS \
{ \
{0x00000000}, /* No registers. */ \
{0x00000003}, /* 't' R0-R1 . */ \
{0x0000000c}, /* 'u' R2-R3 . */ \
{0x000000ff}, /* 'q' R0-R7 . */ \
{0xf00000ff}, /* 'f' R0-R11 */ \
{0x0000ff00}, /* 'a' AR0-AR7. */ \
{0x00060000}, /* 'x' IR0-IR1. */ \
{0x00080000}, /* 'k' BK. */ \
{0x00100000}, /* 'b' SP. */ \
{0x08000000}, /* 'v' RC. */ \
{0x0800ff00}, /* RC,AR0-AR7. */ \
{0x0e1eff00}, /* 'c' AR0-AR7, IR0-IR1, BK, SP, RS, RE, RC. */ \
{0xfe1effff}, /* 'r' R0-R11, AR0-AR7, IR0-IR1, BK, SP, RS, RE, RC. */\
{0x00010000}, /* 'z' DP. */ \
{0x00200000}, /* 'y' ST. */ \
{0xffffffff}, /* All registers. */ \
}
 
/* The same information, inverted:
Return the class number of the smallest class containing
reg number REGNO. This could be a conditional expression
or could index an array. */
 
#define REGNO_REG_CLASS(REGNO) (c4x_regclass_map[REGNO])
 
/* When SMALL_REGISTER_CLASSES is defined, the lifetime of registers
explicitly used in the rtl is kept as short as possible.
 
We only need to define SMALL_REGISTER_CLASSES if TARGET_PARALLEL_MPY
is defined since the MPY|ADD insns require the classes R0R1_REGS and
R2R3_REGS which are used by the function return registers (R0,R1) and
the register arguments (R2,R3), respectively. I'm reluctant to define
this macro since it stomps on many potential optimizations. Ideally
it should have a register class argument so that not all the register
classes gets penalized for the sake of a naughty few... For long
double arithmetic we need two additional registers that we can use as
spill registers. */
 
#define SMALL_REGISTER_CLASSES (TARGET_SMALL_REG_CLASS && TARGET_PARALLEL_MPY)
 
#define BASE_REG_CLASS ADDR_REGS
#define INDEX_REG_CLASS INDEX_REGS
 
/*
Register constraints for the C4x
a - address reg (ar0-ar7)
b - stack reg (sp)
c - other gp int-only reg
d - data/int reg (equiv. to f)
f - data/float reg
h - data/long double reg (equiv. to f)
k - block count (bk)
q - r0-r7
t - r0-r1
u - r2-r3
v - repeat count (rc)
x - index register (ir0-ir1)
y - status register (st)
z - dp reg (dp)
 
Memory/constant constraints for the C4x
 
G - short float 16-bit
I - signed 16-bit constant (sign extended)
J - signed 8-bit constant (sign extended) (C4x only)
K - signed 5-bit constant (sign extended) (C4x only for stik)
L - unsigned 16-bit constant
M - unsigned 8-bit constant (C4x only)
N - ones complement of unsigned 16-bit constant
Q - indirect arx + 9-bit signed displacement
(a *-arx(n) or *+arx(n) is used to account for the sign bit)
R - indirect arx + 5-bit unsigned displacement (C4x only)
S - indirect arx + 0, 1, or irn displacement
T - direct symbol ref
> - indirect with autoincrement
< - indirect with autodecrement
} - indirect with post-modify
{ - indirect with pre-modify
*/
 
#define REG_CLASS_FROM_LETTER(CC) \
( ((CC) == 'a') ? ADDR_REGS \
: ((CC) == 'b') ? SP_REG \
: ((CC) == 'c') ? INT_REGS \
: ((CC) == 'd') ? EXT_REGS \
: ((CC) == 'f') ? EXT_REGS \
: ((CC) == 'h') ? EXT_REGS \
: ((CC) == 'k') ? BK_REG \
: ((CC) == 'q') ? EXT_LOW_REGS \
: ((CC) == 't') ? R0R1_REGS \
: ((CC) == 'u') ? R2R3_REGS \
: ((CC) == 'v') ? RC_REG \
: ((CC) == 'x') ? INDEX_REGS \
: ((CC) == 'y') ? ST_REG \
: ((CC) == 'z') ? DP_REG \
: NO_REGS )
 
/* These assume that REGNO is a hard or pseudo reg number.
They give nonzero only if REGNO is a hard reg of the suitable class
or a pseudo reg currently allocated to a suitable hard reg.
Since they use reg_renumber, they are safe only once reg_renumber
has been allocated, which happens in local-alloc.c. */
 
#define REGNO_OK_FOR_BASE_P(REGNO) \
(IS_ADDR_REGNO(REGNO) || IS_ADDR_REGNO((unsigned)reg_renumber[REGNO]))
 
#define REGNO_OK_FOR_INDEX_P(REGNO) \
(IS_INDEX_REGNO(REGNO) || IS_INDEX_REGNO((unsigned)reg_renumber[REGNO]))
 
/* If we have to generate framepointer + constant prefer an ADDR_REGS
register. This avoids using EXT_REGS in addqi3_noclobber_reload. */
 
#define PREFERRED_RELOAD_CLASS(X, CLASS) \
(GET_CODE (X) == PLUS \
&& GET_MODE (X) == Pmode \
&& GET_CODE (XEXP ((X), 0)) == REG \
&& GET_MODE (XEXP ((X), 0)) == Pmode \
&& REGNO (XEXP ((X), 0)) == FRAME_POINTER_REGNUM \
&& GET_CODE (XEXP ((X), 1)) == CONST_INT \
? ADDR_REGS : (CLASS))
 
#define LIMIT_RELOAD_CLASS(X, CLASS) (CLASS)
 
#define SECONDARY_MEMORY_NEEDED(CLASS1, CLASS2, MODE) 0
 
#define CLASS_MAX_NREGS(CLASS, MODE) \
(((MODE) == CCmode || (MODE) == CC_NOOVmode) ? 1 : ((MODE) == HFmode) ? 1 : \
((GET_MODE_SIZE(MODE) + UNITS_PER_WORD - 1) / UNITS_PER_WORD))
 
#define IS_INT5_CONST(VAL) (((VAL) <= 15) && ((VAL) >= -16)) /* 'K'. */
 
#define IS_UINT5_CONST(VAL) (((VAL) <= 31) && ((VAL) >= 0)) /* 'R'. */
 
#define IS_INT8_CONST(VAL) (((VAL) <= 127) && ((VAL) >= -128)) /* 'J'. */
 
#define IS_UINT8_CONST(VAL) (((VAL) <= 255) && ((VAL) >= 0)) /* 'M'. */
 
#define IS_INT16_CONST(VAL) (((VAL) <= 32767) && ((VAL) >= -32768)) /* 'I'. */
 
#define IS_UINT16_CONST(VAL) (((VAL) <= 65535) && ((VAL) >= 0)) /* 'L'. */
 
#define IS_NOT_UINT16_CONST(VAL) IS_UINT16_CONST(~(VAL)) /* 'N'. */
 
#define IS_HIGH_CONST(VAL) \
(! TARGET_C3X && (((VAL) & 0xffff) == 0)) /* 'O'. */
 
 
#define IS_DISP1_CONST(VAL) (((VAL) <= 1) && ((VAL) >= -1)) /* 'S'. */
 
#define IS_DISP8_CONST(VAL) (((VAL) <= 255) && ((VAL) >= -255)) /* 'Q'. */
 
#define IS_DISP1_OFF_CONST(VAL) (IS_DISP1_CONST (VAL) \
&& IS_DISP1_CONST (VAL + 1))
 
#define IS_DISP8_OFF_CONST(VAL) (IS_DISP8_CONST (VAL) \
&& IS_DISP8_CONST (VAL + 1))
 
#define CONST_OK_FOR_LETTER_P(VAL, C) \
( ((C) == 'I') ? (IS_INT16_CONST (VAL)) \
: ((C) == 'J') ? (! TARGET_C3X && IS_INT8_CONST (VAL)) \
: ((C) == 'K') ? (! TARGET_C3X && IS_INT5_CONST (VAL)) \
: ((C) == 'L') ? (IS_UINT16_CONST (VAL)) \
: ((C) == 'M') ? (! TARGET_C3X && IS_UINT8_CONST (VAL)) \
: ((C) == 'N') ? (IS_NOT_UINT16_CONST (VAL)) \
: ((C) == 'O') ? (IS_HIGH_CONST (VAL)) \
: 0 )
 
#define CONST_DOUBLE_OK_FOR_LETTER_P(OP, C) \
( ((C) == 'G') ? (fp_zero_operand (OP, QFmode)) \
: ((C) == 'H') ? (c4x_H_constant (OP)) \
: 0 )
 
#define EXTRA_CONSTRAINT(OP, C) \
( ((C) == 'Q') ? (c4x_Q_constraint (OP)) \
: ((C) == 'R') ? (c4x_R_constraint (OP)) \
: ((C) == 'S') ? (c4x_S_constraint (OP)) \
: ((C) == 'T') ? (c4x_T_constraint (OP)) \
: ((C) == 'U') ? (c4x_U_constraint (OP)) \
: 0 )
 
#define SMALL_CONST(VAL, insn) \
( ((insn == NULL_RTX) || (get_attr_data (insn) == DATA_INT16)) \
? IS_INT16_CONST (VAL) \
: ( (get_attr_data (insn) == DATA_NOT_UINT16) \
? IS_NOT_UINT16_CONST (VAL) \
: ( (get_attr_data (insn) == DATA_HIGH_16) \
? IS_HIGH_CONST (VAL) \
: IS_UINT16_CONST (VAL) \
) \
) \
)
 
/*
I. Routine calling with arguments in registers
----------------------------------------------
 
The TI C3x compiler has a rather unusual register passing algorithm.
Data is passed in the following registers (in order):
 
AR2, R2, R3, RC, RS, RE
 
However, the first and second floating point values are always in R2
and R3 (and all other floats are on the stack). Structs are always
passed on the stack. If the last argument is an ellipsis, the
previous argument is passed on the stack so that its address can be
taken for the stdargs macros.
 
Because of this, we have to pre-scan the list of arguments to figure
out what goes where in the list.
 
II. Routine calling with arguments on stack
-------------------------------------------
 
Let the subroutine declared as "foo(arg0, arg1, arg2);" have local
variables loc0, loc1, and loc2. After the function prologue has
been executed, the stack frame will look like:
 
[stack grows towards increasing addresses]
I-------------I
5 I saved reg1 I <= SP points here
I-------------I
4 I saved reg0 I
I-------------I
3 I loc2 I
I-------------I
2 I loc1 I
I-------------I
1 I loc0 I
I-------------I
0 I old FP I <= FP (AR3) points here
I-------------I
-1 I return PC I
I-------------I
-2 I arg0 I
I-------------I
-3 I arg1 I
I-------------I
-4 I arg2 I
I-------------I
 
All local variables (locn) are accessible by means of +FP(n+1)
addressing, where n is the local variable number.
 
All stack arguments (argn) are accessible by means of -FP(n-2).
 
The stack pointer (SP) points to the last register saved in the
prologue (regn).
 
Note that a push instruction performs a preincrement of the stack
pointer. (STACK_PUSH_CODE == PRE_INC)
 
III. Registers used in function calling convention
--------------------------------------------------
 
Preserved across calls: R4...R5 (only by PUSH, i.e. lower 32 bits)
R6...R7 (only by PUSHF, i.e. upper 32 bits)
AR3...AR7
 
(Because of this model, we only assign FP values in R6, R7 and
only assign integer values in R4, R5.)
 
These registers are saved at each function entry and restored at
the exit. Also it is expected any of these not affected by any
call to user-defined (not service) functions.
 
Not preserved across calls: R0...R3
R4...R5 (upper 8 bits)
R6...R7 (lower 8 bits)
AR0...AR2, IR0, IR1, BK, ST, RS, RE, RC
 
These registers are used arbitrary in a function without being preserved.
It is also expected that any of these can be clobbered by any call.
 
Not used by GCC (except for in user "asm" statements):
IE (DIE), IF (IIE), IOF (IIF)
 
These registers are never used by GCC for any data, but can be used
with "asm" statements. */
 
#define C4X_ARG0 -2
#define C4X_LOC0 1
 
/* Basic Stack Layout. */
/* The stack grows upward, stack frame grows upward, and args grow
downward. */
 
#define STARTING_FRAME_OFFSET C4X_LOC0
#define FIRST_PARM_OFFSET(FNDECL) (C4X_ARG0 + 1)
#define ARGS_GROW_DOWNWARD
#define STACK_POINTER_OFFSET 1
 
/* Define this if pushing a word on the stack
makes the stack pointer a smaller address. */
 
/* #define STACK_GROWS_DOWNWARD. */
/* Like the dsp16xx, i370, i960, and we32k ports. */
 
/* Define this to nonzero 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 0
 
 
/* 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 \
&& IS_FLOAT_CALL_SAVED_REGNO (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 \
&& IS_FLOAT_CALL_SAVED_REGNO (regno) ? 2 : 1; \
(OFFSET) = -(offset + get_frame_size ()); \
}
 
 
/* Passing Function Arguments on the Stack. */
 
#define PUSH_ARGS 1
#define PUSH_ROUNDING(BYTES) (BYTES)
#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;
 
#define INIT_CUMULATIVE_ARGS(CUM, FNTYPE, LIBNAME, INDIRECT, N_NAMED_ARGS) \
(c4x_init_cumulative_args (&CUM, FNTYPE, LIBNAME))
 
#define FUNCTION_ARG_ADVANCE(CUM, MODE, TYPE, NAMED) \
(c4x_function_arg_advance (&CUM, MODE, TYPE, NAMED))
 
#define FUNCTION_ARG(CUM, MODE, TYPE, NAMED) \
(c4x_function_arg(&CUM, MODE, TYPE, NAMED))
 
/* Define the profitability of saving registers around calls.
We disable caller save to avoid a bug in flow.c (this also affects
other targets such as m68k). Since we must use stf/sti,
the profitability is marginal anyway. */
 
#define CALLER_SAVE_PROFITABLE(REFS,CALLS) 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
 
/* 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.
The FUNCTION profiler needs a function mcount which gets passed
a pointer to the LABELNO. */
 
#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"); \
}
 
/* 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)
 
/* Addressing Modes. */
 
#define HAVE_POST_INCREMENT 1
#define HAVE_PRE_INCREMENT 1
#define HAVE_POST_DECREMENT 1
#define HAVE_PRE_DECREMENT 1
#define HAVE_PRE_MODIFY_REG 1
#define HAVE_POST_MODIFY_REG 1
#define HAVE_PRE_MODIFY_DISP 1
#define HAVE_POST_MODIFY_DISP 1
 
/* The number of insns that can be packed into a single opcode. */
#define PACK_INSNS 2
 
/* Recognize any constant value that is a valid address.
We could allow arbitrary constant addresses in the large memory
model but for the small memory model we can only accept addresses
within the data page. I suppose we could also allow
CONST PLUS SYMBOL_REF. */
#define CONSTANT_ADDRESS_P(X) (GET_CODE (X) == SYMBOL_REF)
 
/* Maximum number of registers that can appear in a valid memory
address. */
#define MAX_REGS_PER_ADDRESS 2
 
/* The macros REG_OK_FOR..._P assume that the arg is a REG rtx
and check its validity for a certain class.
We have two alternate definitions for each of them.
The usual definition accepts all pseudo regs; the other rejects
them unless they have been allocated suitable hard regs.
The symbol REG_OK_STRICT causes the latter definition to be used.
 
Most source files want to accept pseudo regs in the hope that
they will get allocated to the class that the insn wants them to be in.
Source files for reload pass need to be strict.
After reload, it makes no difference, since pseudo regs have
been eliminated by then. */
 
#ifndef REG_OK_STRICT
 
/* Nonzero if X is a hard or pseudo reg that can be used as a base. */
 
#define REG_OK_FOR_BASE_P(X) IS_ADDR_OR_PSEUDO_REG(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(X)
 
#define GO_IF_LEGITIMATE_ADDRESS(MODE, X, ADDR) \
{ \
if (c4x_legitimate_address_p (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_legitimate_address_p (MODE, X, 1)) \
goto ADDR; \
}
 
#endif
 
#define LEGITIMIZE_ADDRESS(X, OLDX, MODE, WIN) \
{ \
rtx new; \
\
new = c4x_legitimize_address (X, MODE); \
if (new != NULL_RTX) \
{ \
(X) = new; \
goto WIN; \
} \
}
 
#define LEGITIMIZE_RELOAD_ADDRESS(X,MODE,OPNUM,TYPE,IND_LEVELS,WIN) \
{ \
if (MODE != HImode \
&& MODE != HFmode \
&& GET_MODE (X) != HImode \
&& GET_MODE (X) != HFmode \
&& (GET_CODE (X) == CONST \
|| GET_CODE (X) == SYMBOL_REF \
|| GET_CODE (X) == LABEL_REF)) \
{ \
if (! TARGET_SMALL) \
{ \
int i; \
(X) = gen_rtx_LO_SUM (GET_MODE (X), \
gen_rtx_HIGH (GET_MODE (X), X), X); \
i = push_reload (XEXP (X, 0), NULL_RTX, \
&XEXP (X, 0), NULL, \
DP_REG, GET_MODE (X), VOIDmode, 0, 0, \
OPNUM, TYPE); \
/* The only valid reg is DP. This is a fixed reg and will \
normally not be used so force it. */ \
rld[i].reg_rtx = gen_rtx_REG (Pmode, DP_REGNO); \
rld[i].nocombine = 1; \
} \
else \
{ \
/* make_memloc in reload will substitute invalid memory \
references. We need to fix them up. */ \
(X) = gen_rtx_LO_SUM (Pmode, gen_rtx_REG (Pmode, DP_REGNO), (X)); \
} \
goto WIN; \
} \
else if (MODE != HImode \
&& MODE != HFmode \
&& GET_MODE (X) != HImode \
&& GET_MODE (X) != HFmode \
&& GET_CODE (X) == LO_SUM \
&& GET_CODE (XEXP (X,0)) == HIGH \
&& (GET_CODE (XEXP (XEXP (X,0),0)) == CONST \
|| GET_CODE (XEXP (XEXP (X,0),0)) == SYMBOL_REF \
|| GET_CODE (XEXP (XEXP (X,0),0)) == LABEL_REF)) \
{ \
if (! TARGET_SMALL) \
{ \
int i = push_reload (XEXP (X, 0), NULL_RTX, \
&XEXP (X, 0), NULL, \
DP_REG, GET_MODE (X), VOIDmode, 0, 0, \
OPNUM, TYPE); \
/* The only valid reg is DP. This is a fixed reg and will \
normally not be used so force it. */ \
rld[i].reg_rtx = gen_rtx_REG (Pmode, DP_REGNO); \
rld[i].nocombine = 1; \
} \
goto WIN; \
} \
}
 
/* No mode-dependent addresses on the C4x are autoincrements. */
 
#define GO_IF_MODE_DEPENDENT_ADDRESS(ADDR, LABEL) \
if (GET_CODE (ADDR) == PRE_DEC \
|| GET_CODE (ADDR) == POST_DEC \
|| GET_CODE (ADDR) == PRE_INC \
|| GET_CODE (ADDR) == POST_INC \
|| GET_CODE (ADDR) == POST_MODIFY \
|| GET_CODE (ADDR) == PRE_MODIFY) \
goto LABEL
 
 
/* Nonzero if the constant value X is a legitimate general operand.
It is given that X satisfies CONSTANT_P or is a CONST_DOUBLE.
 
The C4x can only load 16-bit immediate values, so we only allow a
restricted subset of CONST_INT and CONST_DOUBLE. Disallow
LABEL_REF and SYMBOL_REF (except on the C40 with the big memory
model) so that the symbols will be forced into the constant pool.
On second thoughts, let's do this with the move expanders since
the alias analysis has trouble if we force constant addresses
into memory.
*/
 
#define LEGITIMATE_CONSTANT_P(X) \
((GET_CODE (X) == CONST_DOUBLE && c4x_H_constant (X)) \
|| (GET_CODE (X) == CONST_INT) \
|| (GET_CODE (X) == SYMBOL_REF) \
|| (GET_CODE (X) == LABEL_REF) \
|| (GET_CODE (X) == CONST) \
|| (GET_CODE (X) == HIGH && ! TARGET_C3X) \
|| (GET_CODE (X) == LO_SUM && ! TARGET_C3X))
 
#define LEGITIMATE_DISPLACEMENT_P(X) IS_DISP8_CONST (INTVAL (X))
 
/* Describing Relative Cost of Operations. */
 
#define CANONICALIZE_COMPARISON(CODE, OP0, OP1) \
if (REG_P (OP1) && ! REG_P (OP0)) \
{ \
rtx tmp = OP0; OP0 = OP1 ; OP1 = tmp; \
CODE = swap_condition (CODE); \
}
 
#define EXT_CLASS_P(CLASS) (reg_class_subset_p (CLASS, EXT_REGS))
#define ADDR_CLASS_P(CLASS) (reg_class_subset_p (CLASS, ADDR_REGS))
#define INDEX_CLASS_P(CLASS) (reg_class_subset_p (CLASS, INDEX_REGS))
#define EXPENSIVE_CLASS_P(CLASS) (ADDR_CLASS_P(CLASS) \
|| INDEX_CLASS_P(CLASS) || (CLASS) == SP_REG)
 
/* Compute extra cost of moving data between one register class
and another. */
 
#define REGISTER_MOVE_COST(MODE, FROM, TO) 2
 
/* Memory move cost is same as fast register move. Maybe this should
be bumped up?. */
 
#define MEMORY_MOVE_COST(M,C,I) 4
 
/* Branches are kind of expensive (even with delayed branching) so
make their cost higher. */
 
#define BRANCH_COST 8
 
#define WORD_REGISTER_OPERATIONS
 
/* Dividing the Output into Sections. */
 
#define TEXT_SECTION_ASM_OP "\t.text"
 
#define DATA_SECTION_ASM_OP "\t.data"
 
#define READONLY_DATA_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\""
 
/* Switch into a generic section. */
#define TARGET_ASM_NAMED_SECTION c4x_asm_named_section
 
 
/* Overall Framework of an Assembler File. */
 
#define ASM_COMMENT_START ";"
 
#define ASM_APP_ON ""
#define ASM_APP_OFF ""
 
#define ASM_OUTPUT_ASCII(FILE, PTR, LEN) c4x_output_ascii (FILE, PTR, LEN)
 
/* Output and Generation of Labels. */
 
#define NO_DOT_IN_LABEL /* Only required for TI format. */
 
/* Globalizing directive for a label. */
#define GLOBAL_ASM_OP "\t.global\t"
 
#define ASM_OUTPUT_EXTERNAL(FILE, DECL, NAME) \
c4x_external_ref (NAME)
 
/* The prefix to add to user-visible assembler symbols. */
 
#define USER_LABEL_PREFIX "_"
 
/* 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%lu", PREFIX, (unsigned long)(NUM))
 
/* A C statement to output to the stdio stream STREAM assembler code which
defines (equates) the symbol NAME to have the value VALUE. */
 
#define ASM_OUTPUT_DEF(STREAM, NAME, VALUE) \
do { \
assemble_name (STREAM, NAME); \
fprintf (STREAM, "\t.set\t%s\n", VALUE); \
} while (0)
 
/* 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. */
 
/* Output #ident as a .ident. */
 
#define ASM_OUTPUT_IDENT(FILE, NAME) \
fprintf (FILE, "\t.ident \"%s\"\n", NAME);
 
/* Output of Uninitialized Variables. */
 
/* This says how to output an assembler line to define a local
uninitialized variable. */
 
#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", (int)(ROUNDED)))
 
/* This says how to output an assembler line to define a global
uninitialized variable. */
 
#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", (int)(ROUNDED)))
 
#undef ASM_OUTPUT_BSS
#define ASM_OUTPUT_BSS(FILE, DECL, NAME, SIZE, ALIGN) \
( fputs ("\t.globl\t", FILE), \
assemble_name (FILE, (NAME)), \
fputs ("\n\t.bss\t", FILE), \
assemble_name (FILE, (NAME)), \
fprintf (FILE, ",%u\n", (int)(SIZE)))
 
/* 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" \
}
 
#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) == '#')
 
#define PRINT_OPERAND_ADDRESS(FILE, X) c4x_print_operand_address(FILE, X)
 
/* C4x specific pragmas. */
#define REGISTER_TARGET_PRAGMAS() do { \
c_register_pragma (0, "CODE_SECTION", c4x_pr_CODE_SECTION); \
c_register_pragma (0, "DATA_SECTION", c4x_pr_DATA_SECTION); \
c_register_pragma (0, "FUNC_CANNOT_INLINE", c4x_pr_ignored); \
c_register_pragma (0, "FUNC_EXT_CALLED", c4x_pr_ignored); \
c_register_pragma (0, "FUNC_IS_PURE", c4x_pr_FUNC_IS_PURE); \
c_register_pragma (0, "FUNC_IS_SYSTEM", c4x_pr_ignored); \
c_register_pragma (0, "FUNC_NEVER_RETURNS", c4x_pr_FUNC_NEVER_RETURNS); \
c_register_pragma (0, "FUNC_NO_GLOBAL_ASG", c4x_pr_ignored); \
c_register_pragma (0, "FUNC_NO_IND_ASG", c4x_pr_ignored); \
c_register_pragma (0, "INTERRUPT", c4x_pr_INTERRUPT); \
} while (0)
 
/* 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 1
 
/* Don't use octal since this can confuse gas for the c4x. */
#define PUT_SDB_TYPE(a) fprintf(asm_out_file, "\t.type\t0x%x%s", a, SDB_DELIM)
 
#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)
 
/* Note we output relative line numbers for .ef which gas converts
to absolute line numbers. The TI compiler outputs absolute line numbers
in the .sym directive which gas does not support. */
#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 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) \
{ \
fprintf (FILE, "\tldiu\t0,ar1\n"); \
fprintf (FILE, "\tlsh\t16,ar1\n"); \
fprintf (FILE, "\tor\t0,ar1\n"); \
fprintf (FILE, "\tldiu\t0,ar0\n"); \
fprintf (FILE, "\tbud\tar1\n"); \
fprintf (FILE, "\tlsh\t16,ar0\n"); \
fprintf (FILE, "\tor\t0,ar0\n"); \
fprintf (FILE, "\tor\t1000h,st\n"); \
} \
else \
{ \
fprintf (FILE, "\tlaj\t$+4\n"); \
fprintf (FILE, "\taddi3\t4,r11,ar0\n"); \
fprintf (FILE, "\tlda\t*ar0,ar1\n"); \
fprintf (FILE, "\tlda\t*+ar0(1),ar0\n"); \
fprintf (FILE, "\tbud\tar1\n"); \
fprintf (FILE, "\tnop\n"); \
fprintf (FILE, "\tnop\n"); \
fprintf (FILE, "\tor\t1000h,st\n"); \
fprintf (FILE, "\t.word\t0\n"); \
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_INT (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 (QImode, FNADDR, GEN_INT (0xffff), 0); \
tmp2 = expand_shift (LSHIFT_EXPR, QImode, \
GEN_INT (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_INT (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 (QImode, CXT, GEN_INT (0xffff), 0); \
tmp2 = expand_shift (LSHIFT_EXPR, QImode, \
GEN_INT (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 3
 
#define BSS_SECTION_ASM_OP "\t.bss"
 
#define ASM_OUTPUT_REG_PUSH(FILE, REGNO) \
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) \
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 DBR_OUTPUT_SEQEND(FILE) \
if (final_sequence != NULL_RTX) \
{ \
int count; \
rtx insn = XVECEXP (final_sequence, 0, 0); \
int laj = GET_CODE (insn) == CALL_INSN \
|| (GET_CODE (insn) == INSN \
&& GET_CODE (PATTERN (insn)) == TRAP_IF);\
\
count = dbr_sequence_length(); \
while (count < (laj ? 2 : 3)) \
{ \
fputs("\tnop\n", FILE); \
count++; \
} \
if (laj) \
fputs("\tpush\tr11\n", FILE); \
}
 
#define NO_FUNCTION_CSE
 
/* We don't want a leading tab. */
 
#define ASM_OUTPUT_ASM(FILE, STRING) fprintf (FILE, "%s\n", STRING)
 
/* Define the intrinsic functions for the c3x/c4x. */
 
enum c4x_builtins
{
/* intrinsic name */
C4X_BUILTIN_FIX, /* fast_ftoi */
C4X_BUILTIN_FIX_ANSI, /* ansi_ftoi */
C4X_BUILTIN_MPYI, /* fast_imult (only C3x) */
C4X_BUILTIN_TOIEEE, /* toieee (only C4x) */
C4X_BUILTIN_FRIEEE, /* frieee (only C4x) */
C4X_BUILTIN_RCPF /* fast_invf (only C4x) */
};
 
 
/* Hack to overcome use of libgcc2.c using auto-host.h to determine
HAVE_GAS_HIDDEN. */
#undef HAVE_GAS_HIDDEN
/libgcc.S
0,0 → 1,1502
/* libgcc routines for the Texas Instruments TMS320C[34]x
Copyright (C) 1997,98, 1999 Free Software Foundation, Inc.
 
Contributed by Michael Hayes (m.hayes@elec.canterbury.ac.nz)
and Herman Ten Brugge (Haj.Ten.Brugge@net.HCC.nl).
 
This file is part of GCC.
 
GCC is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the
Free Software Foundation; either version 2, or (at your option) any
later version.
 
In addition to the permissions in the GNU General Public License, the
Free Software Foundation gives you unlimited permission to link the
compiled version of this file into combinations with other programs,
and to distribute those combinations without any restriction coming
from the use of this file. (The General Public License restrictions
do apply in other respects; for example, they cover modification of
the file, and distribution when not linked into a combine
executable.)
 
This file is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
 
You should have received a copy of the GNU General Public License
along with this program; see the file COPYING. If not, write to
the Free Software Foundation, 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA. */
 
; These routines are called using the standard TI register argument
; passing model.
; The following registers do not have to be saved:
; r0, r1, r2, r3, ar0, ar1, ar2, ir0, ir1, bk, rs, rc, re, (r9, r10, r11)
;
; Perform floating point divqf3
;
; This routine performs a reciprocal of the divisor using the method
; described in the C30/C40 user manuals. It then multiplies that
; result by the dividend.
;
; Let r be the reciprocal of the divisor v and let the ith estimate
; of r be denoted by r[i]. An iterative approach can be used to
; improve the estimate of r, given an initial estimate r[0], where
;
; r[i + 1] = r[i] * (2.0 - v * r[i])
;
; The normalized error e[i] at the ith iteration is
;
; e[i] = (r - r[i]) / r = (1 / v - r[i]) * v = (1 - v * r[i])
;
; Note that
;
; e[i + 1] = (1 - v * r[i + 1]) = 1 - 2 * v * r[i] + v^2 + (r[i])^2
; = (1 - v * r[i])^2 = (e[i])^2
 
; r2 dividend, r3 divisor, r0 quotient
; clobbers r1, ar1
#ifdef L_divsf3
.text
.global ___divqf3
___divqf3:
 
#ifdef _TMS320C4x
.if .REGPARM == 0
lda sp,ar0
ldf *-ar0(2), r3
.endif
 
pop ar1 ; Pop return address
 
; r0 = estimate of r, r1 = tmp, r2 = dividend, r3 = divisor
rcpf r3, r0 ; Compute initial estimate r[0]
 
mpyf3 r0, r3, r1 ; r1 = r[0] * v
subrf 2.0, r1 ; r1 = 2.0 - r[0] * v
mpyf r1, r0 ; r0 = r[0] * (2.0 - r[0] * v) = r[1]
; End of 1st iteration (16 bits accuracy)
 
mpyf3 r0, r3, r1 ; r1 = r[1] * v
subrf 2.0, r1 ; r1 = 2.0 - r[1] * v
 
bud ar1 ; Delayed branch
mpyf r1, r0 ; r0 = r[1] * (2.0 - r[1] * v) = r[2]
; End of 2nd iteration (32 bits accuracy)
.if .REGPARM == 0
mpyf *-ar0(1), r0 ; Multiply by the dividend
.else
mpyf r2, r0 ; Multiply by the dividend
.endif
rnd r0
; Branch occurs here
#else
.if .REGPARM == 0
ldiu sp,ar0
ldf *-ar0(2), r3
.endif
 
pop ar1 ; Pop return address
 
; Initial estimate r[0] = 1.0 * 2^(-e - 1)
; where v = m * 2^e
 
; r0 = estimate of r, r1 = tmp, r2 = dividend, r3 = divisor
 
; Calculate initial estimate r[0]
pushf r3
pop r0
not r0 ; r0 = -e
; complement exponent = -e -1
; complement sign (side effect)
; complement mantissa (almost 3 bit accurate)
push r0
popf r0 ; r0 = 1.0 * e^(-e - 1) + inverted mantissa
ldf -1.0, r1 ; undo complement sign bit
xor r1, r0
 
mpyf3 r0, r3, r1 ; r1 = r[0] * v
subrf 2.0, r1 ; r1 = 2.0 - r[0] * v
mpyf r1, r0 ; r0 = r[0] * (2.0 - r[0] * v) = r[1]
; End of 1st iteration
 
mpyf3 r0, r3, r1 ; r1 = r[1] * v
subrf 2.0, r1 ; r1 = 2.0 - r[1] * v
mpyf r1, r0 ; r0 = r[1] * (2.0 - r[1] * v) = r[2]
; End of 2nd iteration
 
mpyf3 r0, r3, r1 ; r1 = r[2] * v
subrf 2.0, r1 ; r1 = 2.0 - r[2] * v
mpyf r1, r0 ; r0 = r[2] * (2.0 - r[2] * v) = r[3]
; End of 3rd iteration
 
rnd r0 ; Minimize error in x[3]'s LSBs
 
; Use modified last iteration
; r[4] = (r[3] * (1.0 - (v * r[3]))) + r[3]
mpyf3 r0, r3, r1 ; r1 = r[3] * v
subrf 1.0, r1 ; r1 = 1.0 - r[3] * v
mpyf r0, r1 ; r1 = r[3] * (1.0 - r[3] * v)
addf r1, r0 ; r0 = r[3] * (1.0 - r[3] * v) + r[3] = r[4]
 
rnd r0 ; Minimize error in x[4]'s LSBs
 
bud ar1 ; Delayed branch
 
.if .REGPARM == 0
ldfu *-ar0(1), r2 ; Dividend in mem has only 24 bits significance
.else
rnd r2 ; Minimize error in reg dividend's LSBs
; since this may have 32 bit significance
.endif
 
mpyf r2, r0 ; Multiply by the dividend
rnd r0 ; Round result to 32 bits
 
; Branch occurs here
#endif
 
#endif
;
; Integer signed division
;
; ar2 dividend, r2 divisor, r0 quotient
; clobbers r1, r3, ar0, ar1, ir0, ir1, rc, rs, re
#ifdef L_divsi3
.text
.global ___divqi3
.ref udivqi3n
___divqi3:
.if .REGPARM == 0
#ifdef _TMS320C4x
lda sp,ar0
#else
ldiu sp,ar0
#endif
ldi *-ar0(1), ar2
ldi *-ar0(2), r2
.endif
 
xor3 ar2, r2, r3 ; Get the sign
absi ar2, r0
bvd divq32
ldi r0, ar2
absi r2, r2
cmpi ar2, r2 ; Divisor > dividend?
 
pop ir1
bhid zero ; If so, return 0
 
;
; Normalize oeprands. Use difference exponents as shift count
; for divisor, and as repeat count for "subc"
;
float ar2, r1 ; Normalize dividend
pushf r1 ; Get as integer
pop ar0
lsh -24, ar0 ; Get exponent
float r2, r1 ; Normalize divisor
pushf r1 ; Get as integer
pop ir0
lsh -24, ir0 ; Get exponent
 
subi ir0, ar0 ; Get difference of exponents
lsh ar0, r2 ; Align divisor with dividend
 
;
; Do count + 1 subtracts and shifts
;
rpts ar0
subc r2, ar2
 
;
; Mask off the lower count+1 bits of ar2
;
subri 31, ar0 ; Shift count is (32 - (ar0 + 1))
lsh ar0, ar2 ; Shift left
negi ar0, ar0
lsh3 ar0, ar2, r0 ; Shift right and put result in r0
 
;
; Check sign and negate result if necessary
;
bud ir1 ; Delayed return
negi r0, r1 ; Negate result
ash -31, r3 ; Check sign
ldinz r1, r0 ; If set, use negative result
; Branch occurs here
 
zero: bud ir1 ; Delayed branch
ldi 0, r0
nop
nop
; Branch occurs here
;
; special case where ar2 = abs(ar2) = 0x80000000. We handle this by
; calling unsigned divide and negating the result if necessary.
;
divq32:
push r3 ; Save sign
call udivqi3n
pop r3
pop ir1
bd ir1
negi r0, r1 ; Negate result
ash -31, r3 ; Check sign
ldinz r1, r0 ; If set, use negative result
; Branch occurs here
#endif
;
;
; ar2 dividend, r2 divisor, r0 quotient,
; clobbers r1, r3, ar0, ar1, ir0, ir1, rc, rs, re
#ifdef L_udivsi3
.text
.global ___udivqi3
.global udivqi3n
___udivqi3:
.if .REGPARM == 0
#ifdef _TMS320C4x
lda sp,ar0
#else
ldiu sp,ar0
#endif
ldi *-ar0(1), ar2
ldi *-ar0(2), r2
.endif
 
udivqi3n:
pop ir1
 
cmpi ar2, r2 ; If divisor > dividend
bhi qzero ; return zero
ldi r2, ar1 ; Store divisor in ar1
 
tstb ar2, ar2 ; Check top bit, jump if set to special handler
bld div_32 ; Delayed branch
 
;
; Get divisor exponent
;
float ar1, r1 ; Normalize the divisor
pushf r1 ; Get into int register
pop rc
; branch occurs here
 
bzd qzero ; if (float) divisor zero, return zero
 
float ar2, r1 ; Normalize the dividend
pushf r1 ; Get into int register
pop ar0
lsh -24, ar0 ; Get both the exponents
lsh -24, rc
 
subi rc, ar0 ; Get the difference between the exponents
lsh ar0, ar1 ; Normalize the divisor with the dividend
 
;
; Do count_1 subtracts and shifts
;
rpts ar0
subc ar1, ar2
 
;
; mask off the lower count+1 bits
;
subri 31, ar0 ; Shift count (31 - (ar0+1))
bud ir1 ; Delayed return
lsh3 ar0, ar2, r0
negi ar0, ar0
lsh ar0, r0
; Branch occurs here
 
;
; Handle a full 32-bit dividend
;
div_32: tstb ar1, ar1
bld qone ; if divisor high bit is one, the result is one
lsh -24, rc
subri 31, rc
lsh rc, ar1 ; Line up the divisor
 
;
; Now divisor and dividend are aligned. Do first SUBC by hand, save
; of the forst quotient digit. Then, shift divisor right rather
; than shifting dividend left. This leaves a zero in the top bit of
; the divident
;
ldi 1, ar0 ; Initizialize MSB of quotient
lsh rc, ar0 ; create a mask for MSBs
subi 1, ar0 ; mask is (2 << count) - 1
 
subi3 ar1, ar2, r1
ldihs r1, ar2
ldihs 1, r1
ldilo 0, r1
lsh rc, r1
 
lsh -1, ar1
subi 1, rc
;
; do the rest of the shifts and subtracts
;
rpts rc
subc ar1, ar2
 
bud ir1
and ar0, ar2
or3 r1, ar2, r0
nop
 
qone:
bud ir1
ldi 1, r0
nop
nop
 
qzero:
bud ir1
ldi 0, r0
nop
nop
#endif
 
#ifdef L_umodsi3
.text
.global ___umodqi3
.global umodqi3n
___umodqi3:
.if .REGPARM == 0
#ifdef _TMS320C4x
lda sp,ar0
#else
ldiu sp,ar0
#endif
ldi *-ar0(1), ar2
ldi *-ar0(2), r2
.endif
 
umodqi3n:
pop ir1 ; return address
cmpi ar2, r2 ; divisor > dividend ?
bhi uzero ; if so, return dividend
ldi r2, ar1 ; load divisor
;
; If top bit of dividend is set, handle specially.
;
tstb ar2, ar2 ; check top bit
bld umod_32 ; get divisor exponent, then jump.
;
; Get divisor exponent by converting to float.
;
float ar1, r1 ; normalize divisor
pushf r1 ; push as float
pop rc ; pop as int to get exponent
bzd uzero ; if (float)divisor was zero, return
;
; 31 or less bits in dividend. Get dividend exponent.
;
float ar2, r1 ; normalize dividend
pushf r1 ; push as float
pop ar0 ; pop as int to get exponent
;
; Use difference in exponents as shift count to line up MSBs.
;
lsh -24, rc ; divisor exponent
lsh -24, ar0 ; dividend exponent
subi rc, ar0 ; difference
lsh ar0, ar1 ; shift divisor up
;
; Do COUNT+1 subtract & shifts.
;
rpts ar0
subc ar1, ar2
;
; Remainder is in upper 31-COUNT bits.
;
bud ir1 ; delayed branch to return
addi 1, ar0 ; shift count is COUNT+1
negi ar0, ar0 ; negate for right shift
lsh3 ar0, ar2, r0 ; shift to get result
; Return occurs here
 
;
; The following code handles cases of a full 32-bit dividend. Before
; SUBC can be used, the top bit must be cleared (otherwise SUBC can
; possibly shift a significant 1 out the top of the dividend). This
; is accomplished by first doing a normal subtraction, then proceeding
; with SUBCs.
;
umod_32:
;
; If the top bit of the divisor is set too, the remainder is simply
; the difference between the dividend and divisor. Otherwise, shift
; the divisor up to line up the MSBs.
;
tstb ar1, ar1 ; check divisor
bld uone ; if negative, remainder is diff
 
lsh -24, rc ; divisor exponent
subri 31, rc ; shift count = 31 - exp
negi rc, ar0 ; used later as shift count
lsh rc, ar1 ; shift up to line up MSBs
;
; Now MSBs are aligned. Do first SUBC by hand using a plain subtraction.
; Then, shift divisor right rather than shifting dividend left. This leaves
; a 0 in the top bit of the dividend.
;
subi3 ar1, ar2, r1 ; subtract
ldihs r1, ar2 ; if positive, replace dividend
subi 1, rc ; first iteration is done
lsh -1, ar1 ; shift divisor down
;
; Do EXP subtract & shifts.
;
rpts rc
subc ar1, ar2
;
; Quotient is in EXP+1 LSBs; shift remainder (in MSBs) down.
;
bud ir1
lsh3 ar0, ar2, r0 ; COUNT contains -(EXP+1)
nop
nop
;
; Return (dividend - divisor).
;
uone: bud ir1
subi3 r2, ar2, r0
nop
nop
;
; Return dividend.
;
uzero: bud ir1
ldi ar2, r0 ; set status from result
nop
nop
#endif
 
#ifdef L_modsi3
.text
.global ___modqi3
.ref umodqi3n
___modqi3:
.if .REGPARM == 0
#ifdef _TMS320C4x
lda sp,ar0
#else
ldiu sp,ar0
#endif
ldi *-ar0(1), ar2
ldi *-ar0(2), r2
.endif
 
;
; Determine sign of result. Get absolute value of operands.
;
ldi ar2, ar0 ; sign of result same as dividend
absi ar2, r0 ; make dividend positive
bvd mod_32 ; if still negative, escape
absi r2, r1 ; make divisor positive
ldi r1, ar1 ; save in ar1
cmpi r0, ar1 ; divisor > dividend ?
 
pop ir1 ; return address
bhid return ; if so, return dividend
;
; Normalize operands. Use difference in exponents as shift count
; for divisor, and as repeat count for SUBC.
;
float r1, r1 ; normalize divisor
pushf r1 ; push as float
pop rc ; pop as int
bzd return ; if (float)divisor was zero, return
 
float r0, r1 ; normalize dividend
pushf r1 ; push as float
pop r1 ; pop as int
 
lsh -24, rc ; get divisor exponent
lsh -24, r1 ; get dividend exponent
subi rc, r1 ; get difference in exponents
lsh r1, ar1 ; align divisor with dividend
;
; Do COUNT+1 subtract & shifts.
;
rpts r1
subc ar1, r0
;
; Remainder is in upper bits of R0
;
addi 1, r1 ; shift count is -(r1+1)
negi r1, r1
lsh r1, r0 ; shift right
;
; Check sign and negate result if necessary.
;
return:
bud ir1 ; delayed branch to return
negi r0, r1 ; negate result
cmpi 0, ar0 ; check sign
ldin r1, r0 ; if set, use negative result
; Return occurs here
;
; The following code handles cases of a full 32-bit dividend. This occurs
; when R0 = abs(R0) = 080000000h. Handle this by calling the unsigned mod
; function, then negating the result if necessary.
;
mod_32:
push ar0 ; remember sign
call umodqi3n ; do divide
 
brd return ; return
pop ar0 ; restore sign
pop ir1 ; return address
nop
#endif
 
#ifdef L_unsfltconst
.section .const
.global ___unsfltconst
___unsfltconst: .float 4294967296.0
#endif
 
#ifdef L_unsfltcompare
.section .const
.global ___unsfltcompare
___unsfltcompare: .float 2147483648.0
#endif
 
; Integer 32-bit signed multiplication
;
; The TMS320C3x MPYI instruction takes two 24-bit signed integers
; and produces a 48-bit signed result which is truncated to 32-bits.
;
; A 32-bit by 32-bit multiplication thus requires a number of steps.
;
; Consider the product of two 32-bit signed integers,
;
; z = x * y
;
; where x = (b << 16) + a, y = (d << 16) + c
;
; This can be expressed as
;
; z = ((b << 16) + a) * ((d << 16) + c)
;
; = ((b * d) << 32) + ((b * c + a * d) << 16) + a * c
;
; Let z = (f << 16) + e where f < (1 << 16).
;
; Since we are only interested in a 32-bit result, we can ignore the
; (b * d) << 32 term, and thus
;
; f = b * c + a * d, e = a * c
;
; We can simplify things if we have some a priori knowledge of the
; operands, for example, if -32768 <= y <= 32767, then y = c and d = 0 and thus
;
; f = b * c, e = a * c
;
; ar2 multiplier, r2 multiplicand, r0 product
; clobbers r1, r2, r3
#ifdef L_mulsi3
.text
.global ___mulqi3
___mulqi3:
.if .REGPARM == 0
#ifdef _TMS320C4x
lda sp,ar0
#else
ldiu sp,ar0
#endif
ldi *-ar0(1), ar2
ldi *-ar0(2), r2
.endif
 
pop ir1 ; return address
ldi ar2, r0 ;
and 0ffffh, r0 ; a
lsh -16, ar2 ; b
ldi r2, r3 ;
and 0ffffh, r3 ; c
mpyi r3, ar2 ; c * b
lsh -16, r2 ; d
mpyi r0, r2 ; a * d
addi ar2, r2 ; c * b + a * d
bd ir1 ; delayed branch to return
lsh 16, r2 ; (c * b + a * d) << 16
mpyi r3, r0 ; a * c
addi r2, r0 ; a * c + (c * b + a * d) << 16
; branch occurs here
 
#endif
 
;
; Integer 64 by 64 multiply
; long1 and long2 on stack
; result in r0,r1
;
#ifdef L_muldi3
.text
.global ___mulhi3
#ifdef _TMS320C4x
___mulhi3:
pop ar0
ldi sp,ar2
ldi *-ar2(1),r2
ldi *-ar2(3),r3
mpyi3 r2,r3,r0
mpyuhi3 r2,r3,r1
mpyi *-ar2(2),r2
bd ar0
mpyi *-ar2(0),r3
addi r2,r1
addi r3,r1
#else
___mulhi3:
ldi sp,ar2
ldi -16,rs
ldi *-ar2(2),ar0
ldi *-ar2(4),ar1
ldi ar0,r2
and 0ffffh,r2
ldi ar1,r3
and 0ffffh,r3
lsh rs,ar0
lsh rs,ar1
 
mpyi r2,r3,r0
mpyi ar0,ar1,r1
mpyi r2,ar1,rc
lsh rs,rc,re
addi re,r1
lsh 16,rc
addi rc,r0
addc 0,r1
mpyi r3,ar0,rc
lsh rs,rc,re
addi re,r1
lsh 16,rc
addi rc,r0
addc 0,r1
 
ldi *-ar2(1),ar0
ldi ar0,r2
and 0ffffh,r2
lsh rs,ar0
mpyi r2,r3,rc
addi rc,r1
mpyi r2,ar1,rc
mpyi r3,ar0,re
addi re,rc
lsh 16,rc
addi rc,r1
 
ldi *-ar2(2),ar0
ldi *-ar2(3),ar1
ldi ar0,r2
and 0ffffh,r2
ldi ar1,r3
and 0ffffh,r3
lsh rs,ar0
lsh rs,ar1
mpyi r2,r3,rc
addi rc,r1
mpyi r2,ar1,rc
mpyi r3,ar0,re
pop ar0
bd ar0
addi re,rc
lsh 16,rc
addi rc,r1
#endif
#endif
 
;
; Integer 32 by 32 multiply highpart unsigned
; src1 in ar2
; src2 in r2
; result in r0
;
#ifdef L_umuldi3_high
.text
.global ___umulhi3_high
___umulhi3_high:
.if .REGPARM == 0
#ifdef _TMS320C4x
lda sp,ar0
#else
ldiu sp,ar0
#endif
ldi *-ar0(1), ar2
ldi *-ar0(2), r2
.endif
 
ldi -16,rs
ldi r2,r3
and 0ffffh,r2
ldi ar2,ar1
and 0ffffh,ar2
lsh rs,r3
lsh rs,ar1
 
mpyi ar2,r2,r1
mpyi ar1,r3,r0
mpyi ar2,r3,rc
lsh rs,rc,re
addi re,r0
lsh 16,rc
addi rc,r1
addc 0,r0
mpyi r2,ar1,rc
lsh rs,rc,re
addi re,r0
pop ar0
bd ar0
lsh 16,rc
addi rc,r1
addc 0,r0
#endif
 
;
; Integer 32 by 32 multiply highpart signed
; src1 in ar2
; src2 in r2
; result in r0
;
#ifdef L_smuldi3_high
.text
.global ___smulhi3_high
___smulhi3_high:
.if .REGPARM == 0
#ifdef _TMS320C4x
lda sp,ar0
#else
ldiu sp,ar0
#endif
ldi *-ar0(1), ar2
ldi *-ar0(2), r2
.endif
 
ldi -16,rs
ldi 0,rc
subi3 ar2,rc,r0
ldi r2,r3
ldilt r0,rc
subi3 r2,rc,r0
ldi ar2,ar1
tstb ar1,ar1
ldilt r0,rc
and 0ffffh,r2
and 0ffffh,ar2
lsh rs,r3
lsh rs,ar1
 
mpyi ar2,r2,r1
mpyi ar1,r3,r0
addi rc,r0
mpyi ar2,r3,rc
lsh rs,rc,re
addi re,r0
lsh 16,rc
addi rc,r1
addc 0,r0
mpyi r2,ar1,rc
lsh rs,rc,re
addi re,r0
pop ar0
bd ar0
lsh 16,rc
addi rc,r1
addc 0,r0
#endif
 
;
; Integer 64 by 64 unsigned divide
; long1 and long2 on stack
; divide in r0,r1
; modulo in r2,r3
; routine takes a maximum of 64*8+23=535 cycles = 21.4 us @ 50Mhz
;
#ifdef L_udivdi3
.text
.global ___udivhi3
.global ___udivide
.global ___umodulo
.ref udivqi3n
.ref umodqi3n
___udivhi3:
ldi sp,ar2
ldi *-ar2(4),ar0
ldi *-ar2(3),ar1
ldi *-ar2(2),r0
ldi *-ar2(1),r1
 
___udivide:
or r1,ar1,r2
bne udiv0
ldi ar0,r2
ldi r0,ar2
call udivqi3n
ldiu 0,r1
rets
 
___umodulo:
or r1,ar1,r2
bne udiv0
ldi ar0,r2
ldi r0,ar2
call umodqi3n
ldi r0,r2
ldiu 0,r3
rets
 
udiv0:
tstb ar1,ar1
bne udiv1
tstb ar0,ar0
bn udiv1
 
ldiu 63,rc
#ifdef _TMS320C4x
rptbd udivend0
ldiu 0,r2
addi r0,r0
rolc r1
#else
ldiu 0,r2
addi r0,r0
rolc r1
rptb udivend0
#endif
 
rolc r2
subi3 ar0,r2,r3
ldinc r3,r2
rolc r0
udivend0:
rolc r1
 
not r0
not r1
ldiu 0,r3
rets
udiv1:
push r4
push r5
ldiu 63,rc
ldiu 0,r2
#ifdef _TMS320C4x
rptbd udivend1
ldiu 0,r3
addi r0,r0
rolc r1
#else
ldiu 0,r3
addi r0,r0
rolc r1
rptb udivend1
#endif
 
rolc r2
rolc r3
subi3 ar0,r2,r4
subb3 ar1,r3,r5
ldinc r4,r2
ldinc r5,r3
rolc r0
udivend1:
rolc r1
 
not r0
not r1
pop r5
pop r4
rets
#endif
 
;
; Integer 64 by 64 unsigned modulo
; long1 and long2 on stack
; result in r0,r1
;
#ifdef L_umoddi3
.text
.global ___umodhi3
.ref ___modulo
___umodhi3:
ldi sp,ar2
ldi *-ar2(4),ar0
ldi *-ar2(3),ar1
ldi *-ar2(2),r0
ldi *-ar2(1),r1
call ___umodulo
pop ar0
bd ar0
ldi r2,r0
ldi r3,r1
nop
#endif
 
;
; Integer 64 by 64 signed divide
; long1 and long2 on stack
; result in r0,r1
;
#ifdef L_divdi3
.text
.global ___divhi3
.ref ___udivide
___divhi3:
ldi 0,ir0
ldi sp,ar2
ldi *-ar2(4),r0
ldi *-ar2(3),r1
bge div1
not ir0
negi r0
negb r1
div1:
ldi r0,ar0
ldi r1,ar1
ldi *-ar2(2),r0
ldi *-ar2(1),r1
bge div2
not ir0
negi r0
negb r1
div2:
call ___udivide
tstb ir0,ir0
bge div3
negi r0
negb r1
div3:
rets
#endif
 
;
; Integer 64 by 64 signed modulo
; long1 and long2 on stack
; result in r0,r1
;
#ifdef L_moddi3
.text
.global ___modhi3
.ref ___umodulo
___modhi3:
ldi 0,ir0
ldi sp,ar2
ldi *-ar2(4),r0
ldi *-ar2(3),r1
bge mod1
not ir0
negi r0
negb r1
mod1:
ldi r0,ar0
ldi r1,ar1
ldi *-ar2(2),r0
ldi *-ar2(1),r1
bge mod2
not ir0
negi r0
negb r1
mod2:
call ___umodulo
ldi r2,r0
ldi r3,r1
tstb ir0,ir0
bge mod3
negi r0
negb r1
mod3:
rets
#endif
 
;
; double to signed long long conversion
; input in r2
; result in r0,r1
;
#ifdef L_fix_truncsfdi2
.text
.global ___fix_truncqfhi2
.ref ufix_truncqfhi2n
___fix_truncqfhi2:
.if .REGPARM == 0
#ifdef _TMS320C4x
lda sp,ar0
#else
ldiu sp,ar0
#endif
ldf *-ar0(1), r2
.endif
 
cmpf 0.0,r2
bge ufix_truncqfhi2n
negf r2
call ufix_truncqfhi2n
negi r0
negb r1
rets
#endif
 
;
; double to unsigned long long conversion
; input in r2
; result in r0,r1
;
#ifdef L_ufix_truncsfdi2
.text
.global ___ufix_truncqfhi2
.global ufix_truncqfhi2n
___ufix_truncqfhi2:
.if .REGPARM == 0
#ifdef _TMS320C4x
lda sp,ar0
#else
ldiu sp,ar0
#endif
ldf *-ar0(1), r2
.endif
 
ufix_truncqfhi2n:
cmpf 0.0,r2
ble ufix1
pushf r2
pop r3
ash -24,r3
subi 31,r3
cmpi 32,r3
bgt ufix1
cmpi -32,r3
ble ufix1
ldi 1,r0
ash 31,r0
or3 r0,r2,r0
ldi r0,r1
lsh3 r3,r0,r0
subi 32,r3
cmpi -32,r3
ldile 0,r1
lsh3 r3,r1,r1
rets
ufix1:
ldi 0,r0
ldi 0,r1
rets
#endif
 
;
; signed long long to double conversion
; input on stack
; result in r0
;
#ifdef L_floatdisf2
.text
.global ___floathiqf2
.ref ufloathiqf2n
___floathiqf2:
ldi sp,ar2
ldi *-ar2(2),r0
ldi *-ar2(1),r1
bge ufloathiqf2n
negi r0
negb r1
call ufloathiqf2n
negf r0
rets
#endif
 
;
; unsigned long long to double conversion
; input on stack
; result in r0
;
#ifdef L_ufloatdisf2
.text
.global ___ufloathiqf2
.global ufloathiqf2n
.ref ___unsfltconst
___ufloathiqf2:
ldi sp,ar2
ldi *-ar2(2),r0
ldi *-ar2(1),r1
ufloathiqf2n:
.if .BIGMODEL
#ifdef _TMS320C4x
ldpk @___unsfltconst
#else
ldp @___unsfltconst
#endif
.endif
ldf @___unsfltconst,r2
float r0
bge uflt1
addf r2,r0
uflt1:
float r1
bge uflt2
addf r2,r1
uflt2:
#ifdef _TMS320C4x
pop r3
bd r3
mpyf r2,r1
addf r1,r0
nop
#else
ldf r1,r3
and 0ffh,r3
norm r3,r3
mpyf r2,r3
pop ar2
bd ar2
addf r3,r0
mpyf r2,r1
addf r1,r0
#endif
#endif
 
;
; long double to signed long long conversion
; input in r2
; result in r0,r1
;
#ifdef L_fix_truncdfdi2
.text
.global ___fix_trunchfhi2
.ref ufix_trunchfhi2n
___fix_trunchfhi2:
.if .REGPARM == 0
#ifdef _TMS320C4x
lda sp,ar0
#else
ldiu sp,ar0
#endif
ldf *-ar0(2), r2
ldi *-ar0(1), r2
.endif
 
cmpf 0.0,r2
bge ufix_trunchfhi2n
negf r2
call ufix_trunchfhi2n
negi r0
negb r1
rets
#endif
 
;
; long double to unsigned long long conversion
; input in r2
; result in r0,r1
;
#ifdef L_ufix_truncdfdi2
.text
.global ___ufix_trunchfhi2
.global ufix_trunchfhi2n
___ufix_trunchfhi2:
.if .REGPARM == 0
#ifdef _TMS320C4x
lda sp,ar0
#else
ldiu sp,ar0
#endif
ldf *-ar0(2), r2
ldi *-ar0(1), r2
.endif
 
ufix_trunchfhi2n:
cmpf 0.0,r2
ble ufixh1
pushf r2
pop r3
ash -24,r3
subi 31,r3
cmpi 32,r3
bgt ufixh1
cmpi -32,r3
ble ufixh1
ldi 1,r0
ash 31,r0
or3 r0,r2,r0
ldi r0,r1
lsh3 r3,r0,r0
subi 32,r3
cmpi -32,r3
ldile 0,r1
lsh3 r3,r1,r1
rets
ufixh1:
ldi 0,r0
ldi 0,r1
rets
#endif
 
;
; signed long long to long double conversion
; input on stack
; result in r0
;
#ifdef L_floatdidf2
.text
.global ___floathihf2
.ref ufloathihf2n
___floathihf2:
ldi sp,ar2
ldi *-ar2(2),r0
ldi *-ar2(1),r1
bge ufloathihf2n
negi r0
negb r1
call ufloathihf2n
negf r0
rets
#endif
 
;
; unsigned long long to double conversion
; input on stack
; result in r0
;
#ifdef L_ufloatdidf2
.text
.global ___ufloathihf2
.global ufloathihf2n
.ref ___unsfltconst
___ufloathihf2:
ldi sp,ar2
ldi *-ar2(2),r0
ldi *-ar2(1),r1
ufloathihf2n
.if .BIGMODEL
#ifdef _TMS320C4x
ldpk @___unsfltconst
#else
ldp @___unsfltconst
#endif
.endif
ldf @___unsfltconst,r2
float r0
bge uflth1
addf r2,r0
uflth1:
float r1
bge uflth2
addf r2,r1
uflth2:
#ifdef _TMS320C4x
pop r3
bd r3
mpyf r2,r1
addf r1,r0
nop
#else
ldf r1,r3
and 0ffh,r3
norm r3,r3
mpyf r2,r3
pop ar2
bd ar2
addf r3,r0
mpyf r2,r1
addf r1,r0
#endif
#endif
 
;
; calculate ffs
; input in ar2
; result in r0
;
#ifdef L_ffs
.global ___ffs
.ref ___unsfltconst
.text
___ffs:
.if .REGPARM == 0
#ifdef _TMS320C4x
lda sp,ar0
#else
ldiu sp,ar0
#endif
ldi *-ar0(1), ar2
.endif
 
negi ar2,r0
and ar2,r0
float r0,r0
ldfu 0.0,r1
.if .BIGMODEL
#ifdef _TMS320C4x
ldpk @___unsfltconst
#else
ldp @___unsfltconst
#endif
.endif
ldflt @___unsfltconst,r1
addf r1,r0
pushf r0
pop r0
pop ar0
bd ar0
ash -24,r0
ldilt -1,r0
addi 1,r0
#endif
 
;
; calculate long double * long double
; input in r2, r3
; output in r0
;
#ifdef L_muldf3
.global ___mulhf3
.text
___mulhf3:
.if .REGPARM == 0
#ifdef _TMS320C4x
lda sp,ar0
#else
ldiu sp,ar0
#endif
ldf *-ar0(2), r2
ldi *-ar0(1), r2
ldf *-ar0(4), r3
ldi *-ar0(3), r3
.endif
 
pop ar2 ; return ad
ldf r2,r0 ; copy lsb0
ldf r3,r1 ; copy lsb1
and 0ffh,r0 ; mask lsb0
and 0ffh,r1 ; mask lsb1
norm r0,r0 ; correct lsb0
norm r1,r1 ; correct lsb1
mpyf r2,r1 ; arg0*lsb1
mpyf r3,r0 ; arg1*lsb0
bd ar2 ; return (delayed)
addf r0,r1 ; arg0*lsb1 + arg1*lsb0
mpyf r2,r3,r0 ; msb0*msb1
addf r1,r0 ; msb0*msb1 + arg0*lsb1 + arg1*lsb0
#endif
 
;
; calculate long double / long double
; r2 dividend, r3 divisor, r0 quotient
;
#ifdef L_divdf3
.global ___divhf3
.text
___divhf3:
.if .REGPARM == 0
#ifdef _TMS320C4x
lda sp,ar0
#else
ldiu sp,ar0
#endif
ldf *-ar0(2), r2
ldi *-ar0(1), r2
ldf *-ar0(4), r3
ldi *-ar0(3), r3
.endif
 
#ifdef _TMS320C4x
pop ar1
rcpf r3, r0
mpyf3 r0, r3, r1
subrf 2.0, r1
mpyf r1, r0
mpyf3 r0, r3, r1
bud ar1
subrf 2.0, r1
mpyf r1, r0
mpyf r2, r0
#else
pop ar1
pushf r3
pop r0
not r0
push r0
popf r0
ldf -1.0, r1
xor r1, r0
 
mpyf3 r0, r3, r1 ; r1 = r[0] * v
subrf 2.0, r1 ; r1 = 2.0 - r[0] * v
mpyf r1, r0 ; r0 = r[0] * (2.0 - r[0] * v) = r[1]
; End of 1st iteration
 
mpyf3 r0, r3, r1 ; r1 = r[1] * v
subrf 2.0, r1 ; r1 = 2.0 - r[1] * v
mpyf r1, r0 ; r0 = r[1] * (2.0 - r[1] * v) = r[2]
; End of 2nd iteration
 
mpyf3 r0, r3, r1 ; r1 = r[2] * v
subrf 2.0, r1 ; r1 = 2.0 - r[2] * v
mpyf r1, r0 ; r0 = r[2] * (2.0 - r[2] * v) = r[3]
; End of 3rd iteration
 
or 080h, r0
rnd r0
 
; mpyf3 r0, r3, r1 ; r1 = r[3] * v
push r4
pushf r4
mpyf r0, r3, r1
 
ldf r0, r4
and 0ffh, r4
norm r4, r4
mpyf r3, r4
addf r4, r1
 
ldf r3, r4
and 0ffh, r4
norm r4, r4
mpyf r0, r4
addf r4, r1
subrf 2.0, r1 ; r1 = 2.0 - r[3] * v
 
mpyf r1, r0, r3 ; r3 = r[3] * (2.0 - r[3] * v) = r[5]
 
ldf r1, r4
and 0ffh, r4
norm r4, r4
mpyf r0, r4
addf r4, r3
 
ldf r0, r4
and 0ffh, r4
norm r4, r4
mpyf r1, r4
addf r4, r3
 
mpyf r2, r3, r0 ; Multiply by the dividend
 
ldf r2, r4
and 0ffh, r4
norm r4, r4
mpyf r3, r4
addf r4, r0
 
ldf r3, r4
and 0ffh, r4
norm r4, r4
mpyf r2, r4
bd ar1
addf r4, r0
 
popf r4
pop r4
#endif
#endif
/c4x-protos.h
0,0 → 1,246
/* Definitions of target machine for GNU compiler. TMS320C[34]x
Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2003, 2004, 2005,
2007 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 GCC.
 
GCC is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3, or (at your option)
any later version.
 
GCC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
 
You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING3. If not see
<http://www.gnu.org/licenses/>. */
 
#ifndef GCC_C4X_PROTOS_H
#define GCC_C4X_PROTOS_H
 
extern void c4x_override_options (void);
 
extern void c4x_optimization_options (int, int);
 
extern void c4x_output_ascii (FILE *, const char *, int);
 
extern int c4x_interrupt_function_p (void);
 
extern void c4x_expand_prologue (void);
 
extern void c4x_expand_epilogue (void);
 
extern int c4x_null_epilogue_p (void);
 
extern void c4x_global_label (const char *);
 
extern void c4x_external_ref (const char *);
 
#ifdef TREE_CODE
extern void c4x_function_arg_advance (CUMULATIVE_ARGS *,
enum machine_mode, tree, int);
 
extern struct rtx_def *c4x_function_arg (CUMULATIVE_ARGS *,
enum machine_mode, tree, int);
 
#endif /* TREE_CODE */
 
 
#if defined(RTX_CODE) && defined(TREE_CODE)
extern void c4x_init_cumulative_args (CUMULATIVE_ARGS *c, tree, rtx);
 
extern rtx c4x_expand_builtin (tree, rtx, rtx, enum machine_mode, int);
 
extern void c4x_init_builtins (void);
 
#endif /* TREE_CODE and RTX_CODE*/
 
 
#ifdef RTX_CODE
extern struct rtx_def *c4x_gen_compare_reg (enum rtx_code, rtx, rtx);
 
extern int c4x_legitimate_address_p (enum machine_mode, rtx, int);
 
extern int c4x_hard_regno_mode_ok (unsigned int, enum machine_mode);
 
extern int c4x_hard_regno_rename_ok (unsigned int, unsigned int);
 
extern struct rtx_def *c4x_legitimize_address (rtx, enum machine_mode);
 
extern void c4x_print_operand (FILE *, rtx, int);
 
extern void c4x_print_operand_address (FILE *, rtx);
 
extern enum reg_class c4x_preferred_reload_class (rtx, enum reg_class);
 
extern struct rtx_def *c4x_operand_subword (rtx, int, int, enum machine_mode);
 
extern char *c4x_output_cbranch (const char *, rtx);
 
extern int c4x_label_conflict (rtx, rtx, rtx);
 
extern int c4x_address_conflict (rtx, rtx, int, int);
 
extern void c4x_rptb_insert (rtx insn);
 
extern int c4x_rptb_nop_p (rtx);
 
extern int c4x_rptb_rpts_p (rtx, rtx);
 
extern int c4x_check_laj_p (rtx);
 
extern int c4x_autoinc_operand (rtx, enum machine_mode);
 
extern int reg_or_const_operand (rtx, enum machine_mode);
 
extern int mixed_subreg_operand (rtx, enum machine_mode);
 
extern int reg_imm_operand (rtx, enum machine_mode);
 
extern int ar0_reg_operand (rtx, enum machine_mode);
 
extern int ar0_mem_operand (rtx, enum machine_mode);
 
extern int ar1_reg_operand (rtx, enum machine_mode);
 
extern int ar1_mem_operand (rtx, enum machine_mode);
 
extern int ar2_reg_operand (rtx, enum machine_mode);
 
extern int ar2_mem_operand (rtx, enum machine_mode);
 
extern int ar3_reg_operand (rtx, enum machine_mode);
 
extern int ar3_mem_operand (rtx, enum machine_mode);
 
extern int ar4_reg_operand (rtx, enum machine_mode);
 
extern int ar4_mem_operand (rtx, enum machine_mode);
 
extern int ar5_reg_operand (rtx, enum machine_mode);
 
extern int ar5_mem_operand (rtx, enum machine_mode);
 
extern int ar6_reg_operand (rtx, enum machine_mode);
 
extern int ar6_mem_operand (rtx, enum machine_mode);
 
extern int ar7_reg_operand (rtx, enum machine_mode);
 
extern int ar7_mem_operand (rtx, enum machine_mode);
 
extern int ir0_reg_operand (rtx, enum machine_mode);
 
extern int ir0_mem_operand (rtx, enum machine_mode);
 
extern int ir1_reg_operand (rtx, enum machine_mode);
 
extern int ir1_mem_operand (rtx, enum machine_mode);
 
extern int group1_reg_operand (rtx, enum machine_mode);
 
extern int group1_mem_operand (rtx, enum machine_mode);
 
extern int arx_reg_operand (rtx, enum machine_mode);
 
extern int not_rc_reg (rtx, enum machine_mode);
 
extern int not_modify_reg (rtx, enum machine_mode);
 
extern int c4x_shiftable_constant (rtx);
 
extern int c4x_immed_float_p (rtx);
 
extern int c4x_a_register (rtx);
 
extern int c4x_x_register (rtx);
 
extern int c4x_H_constant (rtx);
 
extern int c4x_I_constant (rtx);
 
extern int c4x_J_constant (rtx);
 
extern int c4x_K_constant (rtx);
 
extern int c4x_L_constant (rtx);
 
extern int c4x_N_constant (rtx);
 
extern int c4x_O_constant (rtx);
 
extern int c4x_Q_constraint (rtx);
 
extern int c4x_R_constraint (rtx);
 
extern int c4x_S_indirect (rtx);
 
extern int c4x_S_constraint (rtx);
 
extern int c4x_T_constraint (rtx);
 
extern int c4x_U_constraint (rtx);
 
extern void c4x_emit_libcall (rtx, enum rtx_code, enum machine_mode,
enum machine_mode, int, rtx *);
 
extern void c4x_emit_libcall3 (rtx, enum rtx_code, enum machine_mode, rtx *);
 
extern void c4x_emit_libcall_mulhi (rtx, enum rtx_code,
enum machine_mode, rtx *);
 
extern int c4x_emit_move_sequence (rtx *, enum machine_mode);
 
extern int legitimize_operands (enum rtx_code, rtx *, enum machine_mode);
 
extern int valid_operands (enum rtx_code, rtx *, enum machine_mode);
 
extern int valid_parallel_load_store (rtx *, enum machine_mode);
 
extern int valid_parallel_operands_4 (rtx *, enum machine_mode);
 
extern int valid_parallel_operands_5 (rtx *, enum machine_mode);
 
extern int valid_parallel_operands_6 (rtx *, enum machine_mode);
 
extern GTY(()) rtx smulhi3_libfunc;
extern GTY(()) rtx umulhi3_libfunc;
extern GTY(()) rtx fix_truncqfhi2_libfunc;
extern GTY(()) rtx fixuns_truncqfhi2_libfunc;
extern GTY(()) rtx fix_trunchfhi2_libfunc;
extern GTY(()) rtx fixuns_trunchfhi2_libfunc;
extern GTY(()) rtx floathiqf2_libfunc;
extern GTY(()) rtx floatunshiqf2_libfunc;
extern GTY(()) rtx floathihf2_libfunc;
extern GTY(()) rtx floatunshihf2_libfunc;
 
extern GTY(()) rtx c4x_compare_op0; /* Operand 0 for comparisons. */
extern GTY(()) rtx c4x_compare_op1; /* Operand 1 for comparisons. */
 
#endif /* RTX_CODE */
 
/* Smallest class containing REGNO. */
extern enum reg_class c4x_regclass_map[FIRST_PSEUDO_REGISTER];
extern enum machine_mode c4x_caller_save_map[FIRST_PSEUDO_REGISTER];
 
extern void c4x_pr_CODE_SECTION (struct cpp_reader *);
extern void c4x_pr_DATA_SECTION (struct cpp_reader *);
extern void c4x_pr_FUNC_IS_PURE (struct cpp_reader *);
extern void c4x_pr_FUNC_NEVER_RETURNS (struct cpp_reader *);
extern void c4x_pr_INTERRUPT (struct cpp_reader *);
extern void c4x_pr_ignored (struct cpp_reader *);
extern void c4x_init_pragma (int (*) (tree *));
 
extern GTY(()) tree code_tree;
extern GTY(()) tree data_tree;
extern GTY(()) tree pure_tree;
extern GTY(()) tree noreturn_tree;
extern GTY(()) tree interrupt_tree;
 
#endif /* ! GCC_C4X_PROTOS_H */
/t-rtems
0,0 → 1,10
# Custom RTEMS multilibs
 
# We'd actually want to support -msmall, but it trips a bug in gcc
# http://gcc.gnu.org/bugzilla/show_bug.cgi?id=14436
#
# MULTILIB_OPTIONS = m30 msmall mmemparm
# MULTILIB_DIRNAMES = c3x small mem
 
MULTILIB_OPTIONS = m30 mmemparm
MULTILIB_DIRNAMES = c3x mem
/c4x.md
0,0 → 1,7306
;; Machine description for the TMS320C[34]x for GCC
;; Copyright (C) 1994, 1995, 1996, 1997, 1998,
;; 1999, 2000, 2002, 2004, 2005, 2007 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 GCC.
 
;; GCC is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation; either version 3, or (at your option)
;; any later version.
 
;; GCC is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
 
;; You should have received a copy of the GNU General Public License
;; along with GCC; see the file COPYING3. If not see
;; <http://www.gnu.org/licenses/>.
 
;
; TODO :
; Try using PQImode again for addresses since C30 only uses
; 24-bit addresses. Ideally GCC would emit different insns
; for QImode and Pmode, whether Pmode was QImode or PQImode.
; For addresses we wouldn't have to have a clobber of the CC
; associated with each insn and we could use MPYI in address
; calculations without having to synthesize 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
 
; Additional C40 instructions not coded:
; LDEP, LDPE, LWRct, LAJcond, RETIcondD
 
;
; C4x MODES
;
; QImode char, short, int, long (32-bits)
; HImode long long (64-bits)
; QFmode float, double (32-bits)
; HFmode long double (40-bits)
; CCmode
; CC_NOOVmode
 
;
; C4x PREDICATES:
;
; comparison_operator LT, GT, LE, GE, LTU, GTU, LEU, GEU, EQ, NE
; memory_operand memory [m]
; immediate_operand immediate constant [IKN]
; register_operand register [rf]
; general_operand register, memory, constant [rfmI]
 
; addr_reg_operand AR0-AR7, pseudo reg [a]
; sp_reg_operand SP [b]
; std_reg_operand AR0-AR7, IR0-IR1, RC, RS, RE, SP, pseudo [c]
; ext_reg_operand R0-R11, pseudo reg [f]
; ext_low_reg_operand R0-R7, pseudo reg [q]
; index_reg_operand IR0-IR1, pseudo reg [x]
; st_reg_operand ST [y]
; dp_reg_operand DP [z]
; stik_const_operand 5-bit const [K]
; src_operand general operand [rfHmI]
; par_ind_operand indirect S mode (ARx + 0, 1, IRx) [S<>]
; parallel_operand par_ind_operand or ext_low_reg_operand
; symbolic_address_operand
; call_address_operand
 
; ADDI src2, src1, dst three operand op
; ADDI src, dst two operand op
 
; Note that the predicates are only used when selecting a pattern
; to determine if an operand is valid.
 
; The constraints then select which of the possible valid operands
; is present (and guide register selection). The actual assembly
; instruction is then selected on the basis of the constraints.
 
; The extra constraint (valid_operands) is used to determine if
; the combination of operands is legitimate for the pattern.
 
;
; C4x CONSTRAINTS:
;
; a address reg AR0-AR7
; b stack pointer SP
; c other int reg AR0-AR7, IR0-IR1, RC, RS, RE
; d fp reg R0-R11 (sets CC when dst)
; e
; f fp reg R0-R11 (sets CC when dst)
; g general reg, memory, constant
; h fp reg (HFmode) R0-R11 (sets CC when dst)
; i immediate int constant
; j
; k block count BK
; l
; m memory
; n immediate int constant with known numeric value
; o offsettable memory
; p memory address
; q low fp reg R0-R7 (sets CC when dst)
; r general reg R0-R11, AR0-AR7, IR0-IR1, RC, RS, RE
; s immediate int constant (value not explicit)
; t R0-R1
; u R2-R3
; v repeat count reg RC
; w
; x index reg IR0-IR1
; y status (CC) reg ST
; z data pointer DP
 
; G fp zero
; H fp 16-bit constant
; I signed 16-bit
; J signed 8-bit (C4x only)
; K signed 5-bit (C4x only)
; L unsigned 16-bit
; M unsigned 8-bit (C4x only)
; N ones complement of unsigned 16-bit
; O 16 bit high constant
; Q ARx + 9-bit signed disp
; R ARx + 5-bit unsigned disp (C4x only)
; S ARx + 0, 1, IRx disp
; T direct memory operand
; V non offsettable memory
; X any operand
; < memory operand with autodecrement addressing
; > memory operand with autoincrement addressing
; { memory operand with pre-modify addressing
; } memory operand with post-modify addressing
 
; Note that the 'd', 'f', and 'h' constraints are equivalent.
; The m constraint is equivalent to 'QT<>{}'
 
; Note we cannot use the 'g' constraint with Pmode (i.e, QImode)
; operations since LEGITIMATE_CONSTANT_P accepts SYMBOL_REF.
; So instead we use 'rIm' for signed operands or 'rLm' for unsigned operands.
 
; Note that the constraints are used to select the operands
; for a chosen pattern. The constraint that requires the fewest
; instructions to load an operand is chosen.
 
; Note that the 'r' constraint is mostly only used for src integer register
; operands, while 'c' and 'd' constraints are generally only used for dst
; integer register operands (the 'r' constraint is the union of the 'c' and
; 'd' constraints). When a register satisfying the 'd' constraint
; is used as a dst operand, the CC gets clobbered (except for LDIcond)---but
; not for 'c'.
 
; The 'f' constraint is only for float register operands---when
; a register satisfying the 'f' constraint is used as a dst operand,
; the CC gets clobbered (except for LDFcond).
 
; The ! in front of the 'b' constraint 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 explicitly 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 chooses 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 a 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 cannot be executed in the first delay slot
; and ldpk cannot be executed in the first two delay slots.
 
(define_attr "onlyreg" "false,true"
(cond [(eq_attr "type" "unary,unarycc")
(if_then_else (and (match_operand 0 "reg_imm_operand" "")
(match_operand 1 "reg_imm_operand" ""))
(const_string "true") (const_string "false"))
(eq_attr "type" "binary,binarycc")
(if_then_else (and (match_operand 0 "reg_imm_operand" "")
(and (match_operand 1 "reg_imm_operand" "")
(match_operand 2 "reg_imm_operand" "")))
(const_string "true") (const_string "false"))]
(const_string "false")))
 
(define_attr "onlyreg_nomod" "false,true"
(cond [(eq_attr "type" "unary,unarycc,compare,lda,store")
(if_then_else (and (match_operand 0 "not_modify_reg" "")
(match_operand 1 "not_modify_reg" ""))
(const_string "true") (const_string "false"))
(eq_attr "type" "binary,binarycc")
(if_then_else (and (match_operand 0 "not_modify_reg" "")
(and (match_operand 1 "not_modify_reg" "")
(match_operand 2 "not_modify_reg" "")))
(const_string "true") (const_string "false"))]
(const_string "false")))
 
(define_attr "not_repeat_reg" "false,true"
(cond [(eq_attr "type" "unary,unarycc,compare,lda,ldp,store")
(if_then_else (and (match_operand 0 "not_rc_reg" "")
(match_operand 1 "not_rc_reg" ""))
(const_string "true") (const_string "false"))
(eq_attr "type" "binary,binarycc")
(if_then_else (and (match_operand 0 "not_rc_reg" "")
(and (match_operand 1 "not_rc_reg" "")
(match_operand 2 "not_rc_reg" "")))
(const_string "true") (const_string "false"))]
(const_string "false")))
 
/* Disable compare because the c4x contains a bug. The cmpi insn sets the CC
in the read phase of the pipeline instead of the execution phase when
two registers are compared. */
(define_attr "in_annul_slot_1" "false,true"
(if_then_else (and (and (eq_attr "cpu" "c4x")
(eq_attr "type" "!jump,call,rets,jmpc,compare,db,dbc,repeat,repeat_top,laj,push,pop,lda,ldp,multi"))
(eq_attr "onlyreg" "true"))
(const_string "true")
(const_string "false")))
 
(define_attr "in_annul_slot_2" "false,true"
(if_then_else (and (and (eq_attr "cpu" "c4x")
(eq_attr "type" "!jump,call,rets,jmpc,db,dbc,repeat,repeat_top,laj,push,pop,ldp,multi"))
(eq_attr "onlyreg_nomod" "true"))
(const_string "true")
(const_string "false")))
 
/* Disable ldp because the c4x contains a bug. The ldp insn modifies
the dp register when the insn is anulled or not.
Also disable autoincrement insns because of a silicon bug. */
(define_attr "in_annul_slot_3" "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_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
;
(define_constants
[
(UNSPEC_BU 1)
(UNSPEC_RPTS 2)
(UNSPEC_LSH 3)
(UNSPEC_CMPHI 4)
(UNSPEC_RCPF 5)
(UNSPEC_RND 6)
(UNSPEC_RPTB_FILL 7)
(UNSPEC_LOADHF_INT 8)
(UNSPEC_STOREHF_INT 9)
(UNSPEC_RSQRF 10)
(UNSPEC_LOADQF_INT 11)
(UNSPEC_STOREQF_INT 12)
(UNSPEC_LDIV 13)
(UNSPEC_PUSH_ST 14)
(UNSPEC_POP_ST 15)
(UNSPEC_PUSH_DP 16)
(UNSPEC_POP_DP 17)
(UNSPEC_POPQI 18)
(UNSPEC_POPQF 19)
(UNSPEC_ANDN_ST 20)
(UNSPEC_RPTB_INIT 22)
(UNSPEC_TOIEEE 23)
(UNSPEC_FRIEEE 24)
])
 
;
; C4x PIPELINE MODEL
;
; 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.
;
; 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_insn_reservation "any_insn" 1 (const_int 1) "nothing")
(define_insn_reservation "slowest_insn" 3 (const_int 0) "nothing")
 
; 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_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_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_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_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_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_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_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_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_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_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)))
 
(include "predicates.md")
 
;
; C4x INSN PATTERNS:
;
; Note that the movMM and addP patterns can be called during reload
; so we need to take special care with theses patterns since
; we cannot blindly clobber CC or generate new pseudo registers.
 
;
; TWO OPERAND INTEGER INSTRUCTIONS
;
 
;
; LDP/LDPK
;
(define_insn "set_ldp"
[(set (match_operand:QI 0 "dp_reg_operand" "=z")
(high:QI (match_operand:QI 1 "" "")))]
"! TARGET_SMALL"
"* return (TARGET_C3X) ? \"ldp\\t%A1\" : \"ldpk\\t%A1\";"
[(set_attr "type" "ldp")])
 
(define_insn "set_ldp_prologue"
[(set (match_operand:QI 0 "dp_reg_operand" "=z")
(high:QI (match_operand:QI 1 "" "")))]
"TARGET_SMALL && TARGET_PARANOID"
"* return (TARGET_C3X) ? \"ldp\\t@data_sec\" : \"ldpk\\t@data_sec\";"
[(set_attr "type" "ldp")])
 
(define_insn "set_high"
[(set (match_operand:QI 0 "std_reg_operand" "=c")
(high:QI (match_operand:QI 1 "symbolic_address_operand" "")))]
"! TARGET_C3X && ! TARGET_TI"
"ldhi\\t^%H1,%0"
[(set_attr "type" "unary")])
 
(define_insn "set_lo_sum"
[(set (match_operand:QI 0 "std_reg_operand" "+c")
(lo_sum:QI (match_dup 0)
(match_operand:QI 1 "symbolic_address_operand" "")))]
"! TARGET_TI"
"or\\t#%H1,%0"
[(set_attr "type" "unary")])
 
(define_split
[(set (match_operand:QI 0 "std_reg_operand" "")
(match_operand:QI 1 "symbolic_address_operand" ""))]
"reload_completed && ! TARGET_C3X && ! TARGET_TI"
[(set (match_dup 0) (high:QI (match_dup 1)))
(set (match_dup 0) (lo_sum:QI (match_dup 0) (match_dup 1)))]
"")
 
(define_split
[(set (match_operand:QI 0 "reg_operand" "")
(match_operand:QI 1 "const_int_operand" ""))
(clobber (reg:QI 16))]
"! TARGET_C3X
&& ! IS_INT16_CONST (INTVAL (operands[1]))
&& ! IS_HIGH_CONST (INTVAL (operands[1]))
&& reload_completed
&& std_reg_operand (operands[0], QImode)"
[(set (match_dup 0) (match_dup 2))
(set (match_dup 0) (ior:QI (match_dup 0) (match_dup 3)))]
"
{
operands[2] = GEN_INT (INTVAL (operands[1]) & ~0xffff);
operands[3] = GEN_INT (INTVAL (operands[1]) & 0xffff);
}")
 
(define_split
[(set (match_operand:QI 0 "reg_operand" "")
(match_operand:QI 1 "const_int_operand" ""))]
"! TARGET_C3X
&& ! IS_INT16_CONST (INTVAL (operands[1]))
&& ! IS_HIGH_CONST (INTVAL (operands[1]))
&& reload_completed
&& std_reg_operand (operands[0], QImode)"
[(set (match_dup 0) (match_dup 2))
(set (match_dup 0) (ior:QI (match_dup 0) (match_dup 3)))]
"
{
operands[2] = GEN_INT (INTVAL (operands[1]) & ~0xffff);
operands[3] = GEN_INT (INTVAL (operands[1]) & 0xffff);
}")
 
(define_split
[(set (match_operand:QI 0 "reg_operand" "")
(match_operand:QI 1 "const_int_operand" ""))
(clobber (reg:QI 16))]
"TARGET_C3X && ! TARGET_SMALL
&& ! IS_INT16_CONST (INTVAL (operands[1]))
&& reload_completed
&& std_reg_operand (operands[0], QImode)
&& c4x_shiftable_constant (operands[1]) < 0"
[(set (match_dup 0) (match_dup 2))
(set (match_dup 0) (ashift:QI (match_dup 0) (match_dup 4)))
(set (match_dup 0) (ior:QI (match_dup 0) (match_dup 3)))]
"
{
/* Generate two's complement value of 16 MSBs. */
operands[2] = GEN_INT ((((INTVAL (operands[1]) >> 16) & 0xffff)
- 0x8000) ^ ~0x7fff);
operands[3] = GEN_INT (INTVAL (operands[1]) & 0xffff);
operands[4] = GEN_INT (16);
}")
 
(define_split
[(set (match_operand:QI 0 "reg_operand" "")
(match_operand:QI 1 "const_int_operand" ""))]
"TARGET_C3X && ! TARGET_SMALL
&& ! IS_INT16_CONST (INTVAL (operands[1]))
&& reload_completed
&& std_reg_operand (operands[0], QImode)
&& c4x_shiftable_constant (operands[1]) < 0"
[(set (match_dup 0) (match_dup 2))
(set (match_dup 0) (ashift:QI (match_dup 0) (match_dup 4)))
(set (match_dup 0) (ior:QI (match_dup 0) (match_dup 3)))]
"
{
/* Generate two's complement value of 16 MSBs. */
operands[2] = GEN_INT ((((INTVAL (operands[1]) >> 16) & 0xffff)
- 0x8000) ^ ~0x7fff);
operands[3] = GEN_INT (INTVAL (operands[1]) & 0xffff);
operands[4] = GEN_INT (16);
}")
 
(define_split
[(set (match_operand:QI 0 "reg_operand" "")
(match_operand:QI 1 "const_int_operand" ""))
(clobber (reg:QI 16))]
"TARGET_C3X
&& ! IS_INT16_CONST (INTVAL (operands[1]))
&& reload_completed
&& std_reg_operand (operands[0], QImode)
&& c4x_shiftable_constant (operands[1]) >= 0"
[(set (match_dup 0) (match_dup 2))
(set (match_dup 0) (ashift:QI (match_dup 0) (match_dup 3)))]
"
{
/* Generate two's complement value of MSBs. */
int shift = c4x_shiftable_constant (operands[1]);
 
operands[2] = GEN_INT ((((INTVAL (operands[1]) >> shift) & 0xffff)
- 0x8000) ^ ~0x7fff);
operands[3] = GEN_INT (shift);
}")
 
(define_split
[(set (match_operand:QI 0 "reg_operand" "")
(match_operand:QI 1 "const_int_operand" ""))]
"TARGET_C3X
&& ! IS_INT16_CONST (INTVAL (operands[1]))
&& reload_completed
&& std_reg_operand (operands[0], QImode)
&& c4x_shiftable_constant (operands[1]) >= 0"
[(set (match_dup 0) (match_dup 2))
(set (match_dup 0) (ashift:QI (match_dup 0) (match_dup 3)))]
"
{
/* Generate two's complement value of MSBs. */
int shift = c4x_shiftable_constant (operands[1]);
 
operands[2] = GEN_INT ((((INTVAL (operands[1]) >> shift) & 0xffff)
- 0x8000) ^ ~0x7fff);
operands[3] = GEN_INT (shift);
}")
 
(define_split
[(set (match_operand:QI 0 "reg_operand" "")
(match_operand:QI 1 "const_int_operand" ""))
(clobber (reg:QI 16))]
"! TARGET_SMALL
&& ! IS_INT16_CONST (INTVAL (operands[1]))
&& ! IS_HIGH_CONST (INTVAL (operands[1]))
&& reload_completed
&& ! std_reg_operand (operands[0], QImode)"
[(set (match_dup 2) (high:QI (match_dup 3)))
(set (match_dup 0) (match_dup 4))
(use (match_dup 1))]
"
{
rtx dp_reg = gen_rtx_REG (Pmode, DP_REGNO);
operands[2] = dp_reg;
operands[3] = force_const_mem (Pmode, operands[1]);
operands[4] = change_address (operands[3], QImode,
gen_rtx_LO_SUM (Pmode, dp_reg,
XEXP (operands[3], 0)));
operands[3] = XEXP (operands[3], 0);
}")
 
(define_split
[(set (match_operand:QI 0 "reg_operand" "")
(match_operand:QI 1 "const_int_operand" ""))]
"! TARGET_SMALL
&& ! IS_INT16_CONST (INTVAL (operands[1]))
&& ! IS_HIGH_CONST (INTVAL (operands[1]))
&& reload_completed
&& ! std_reg_operand (operands[0], QImode)"
[(set (match_dup 2) (high:QI (match_dup 3)))
(set (match_dup 0) (match_dup 4))
(use (match_dup 1))]
"
{
rtx dp_reg = gen_rtx_REG (Pmode, DP_REGNO);
operands[2] = dp_reg;
operands[3] = force_const_mem (Pmode, operands[1]);
operands[4] = change_address (operands[3], QImode,
gen_rtx_LO_SUM (Pmode, dp_reg,
XEXP (operands[3], 0)));
operands[3] = XEXP (operands[3], 0);
}")
 
(define_split
[(set (match_operand:QI 0 "reg_operand" "")
(match_operand:QI 1 "const_int_operand" ""))
(clobber (reg:QI 16))]
"TARGET_SMALL
&& ! IS_INT16_CONST (INTVAL (operands[1]))
&& ! IS_HIGH_CONST (INTVAL (operands[1]))
&& reload_completed
&& ((TARGET_C3X && c4x_shiftable_constant (operands[1]) < 0)
|| ! std_reg_operand (operands[0], QImode))"
[(set (match_dup 0) (match_dup 2))
(use (match_dup 1))]
"
{
rtx dp_reg = gen_rtx_REG (Pmode, DP_REGNO);
operands[2] = force_const_mem (Pmode, operands[1]);
operands[2] = change_address (operands[2], QImode,
gen_rtx_LO_SUM (Pmode, dp_reg,
XEXP (operands[2], 0)));
}")
 
(define_split
[(set (match_operand:QI 0 "reg_operand" "")
(match_operand:QI 1 "const_int_operand" ""))]
"TARGET_SMALL
&& ! IS_INT16_CONST (INTVAL (operands[1]))
&& ! IS_HIGH_CONST (INTVAL (operands[1]))
&& reload_completed
&& ((TARGET_C3X && c4x_shiftable_constant (operands[1]) < 0)
|| ! std_reg_operand (operands[0], QImode))"
[(set (match_dup 0) (match_dup 2))
(use (match_dup 1))]
"
{
rtx dp_reg = gen_rtx_REG (Pmode, DP_REGNO);
operands[2] = force_const_mem (Pmode, operands[1]);
operands[2] = change_address (operands[2], QImode,
gen_rtx_LO_SUM (Pmode, dp_reg,
XEXP (operands[2], 0)));
}")
 
(define_split
[(set (match_operand:HI 0 "reg_operand" "")
(match_operand:HI 1 "const_int_operand" ""))
(clobber (reg:QI 16))]
"reload_completed"
[(set (match_dup 2) (match_dup 4))
(set (match_dup 3) (match_dup 5))]
"
{
operands[2] = c4x_operand_subword (operands[0], 0, 1, HImode);
operands[3] = c4x_operand_subword (operands[0], 1, 1, HImode);
operands[4] = c4x_operand_subword (operands[1], 0, 1, HImode);
operands[5] = c4x_operand_subword (operands[1], 1, 1, HImode);
}")
 
 
; We need to clobber the DP reg to be safe in case we
; need to load this address from memory
(define_insn "load_immed_address"
[(set (match_operand:QI 0 "reg_operand" "=a?x?c*r")
(match_operand:QI 1 "symbolic_address_operand" ""))
(clobber (reg:QI 16))]
"TARGET_LOAD_ADDRESS"
"#"
[(set_attr "type" "multi")])
 
 
(define_split
[(set (match_operand:QI 0 "std_reg_operand" "")
(match_operand:QI 1 "symbolic_address_operand" ""))
(clobber (reg:QI 16))]
"reload_completed && ! TARGET_C3X && ! TARGET_TI"
[(set (match_dup 0) (high:QI (match_dup 1)))
(set (match_dup 0) (lo_sum:QI (match_dup 0) (match_dup 1)))]
"")
 
; CC has been selected to load a symbolic address. We force the address
; into memory and then generate LDP and LDIU insns.
; This is also required for the C30 if we pretend that we can
; easily load symbolic addresses into a register.
(define_split
[(set (match_operand:QI 0 "reg_operand" "")
(match_operand:QI 1 "symbolic_address_operand" ""))
(clobber (reg:QI 16))]
"reload_completed
&& ! TARGET_SMALL
&& (TARGET_C3X || TARGET_TI || ! std_reg_operand (operands[0], QImode))"
[(set (match_dup 2) (high:QI (match_dup 3)))
(set (match_dup 0) (match_dup 4))
(use (match_dup 1))]
"
{
rtx dp_reg = gen_rtx_REG (Pmode, DP_REGNO);
operands[2] = dp_reg;
operands[3] = force_const_mem (Pmode, operands[1]);
operands[4] = change_address (operands[3], QImode,
gen_rtx_LO_SUM (Pmode, dp_reg,
XEXP (operands[3], 0)));
operands[3] = XEXP (operands[3], 0);
}")
 
; This pattern is similar to the above but does not emit a LDP
; for the small memory model.
(define_split
[(set (match_operand:QI 0 "reg_operand" "")
(match_operand:QI 1 "symbolic_address_operand" ""))
(clobber (reg:QI 16))]
"reload_completed
&& TARGET_SMALL
&& (TARGET_C3X || TARGET_TI || ! std_reg_operand (operands[0], QImode))"
[(set (match_dup 0) (match_dup 2))
(use (match_dup 1))]
"
{
rtx dp_reg = gen_rtx_REG (Pmode, DP_REGNO);
operands[2] = force_const_mem (Pmode, operands[1]);
operands[2] = change_address (operands[2], QImode,
gen_rtx_LO_SUM (Pmode, dp_reg,
XEXP (operands[2], 0)));
}")
 
(define_insn "loadhi_big_constant"
[(set (match_operand:HI 0 "reg_operand" "=c*d")
(match_operand:HI 1 "const_int_operand" ""))
(clobber (reg:QI 16))]
""
"#"
[(set_attr "type" "multi")])
 
;
; LDIU/LDA/STI/STIK
;
; The following moves will not set the condition codes register.
;
 
; This must come before the general case
(define_insn "*movqi_stik"
[(set (match_operand:QI 0 "memory_operand" "=m")
(match_operand:QI 1 "stik_const_operand" "K"))]
"! TARGET_C3X"
"stik\\t%1,%0"
[(set_attr "type" "store")])
 
(define_insn "loadqi_big_constant"
[(set (match_operand:QI 0 "reg_operand" "=c*d")
(match_operand:QI 1 "const_int_operand" ""))
(clobber (reg:QI 16))]
"! IS_INT16_CONST (INTVAL (operands[1]))
&& ! IS_HIGH_CONST (INTVAL (operands[1]))"
"#"
[(set_attr "type" "multi")])
 
; 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 "dst_operand" "=d,*c,m,r")
(match_operand:QI 1 "src_hi_operand" "rIm,rIm,r,O"))]
"(REG_P (operands[0]) || REG_P (operands[1])
|| GET_CODE (operands[0]) == SUBREG
|| GET_CODE (operands[1]) == SUBREG)
&& ! symbolic_address_operand (operands[1], QImode)"
"*
if (which_alternative == 2)
return \"sti\\t%1,%0\";
 
if (! TARGET_C3X && which_alternative == 3)
{
operands[1] = GEN_INT ((INTVAL (operands[1]) >> 16) & 0xffff);
return \"ldhi\\t%1,%0\";
}
 
/* The lda instruction cannot use the same register as source
and destination. */
if (! TARGET_C3X && which_alternative == 1
&& ( IS_ADDR_REG (operands[0])
|| IS_INDEX_REG (operands[0])
|| IS_SP_REG (operands[0]))
&& (REGNO (operands[0]) != REGNO (operands[1])))
return \"lda\\t%1,%0\";
return \"ldiu\\t%1,%0\";
"
[(set_attr "type" "unary,lda,store,unary")
(set_attr "data" "int16,int16,int16,high_16")])
 
;
; LDI
;
 
; We shouldn't need these peepholes, but the combiner seems to miss them...
(define_peephole
[(set (match_operand:QI 0 "ext_reg_operand" "=d")
(match_operand:QI 1 "src_operand" "rIm"))
(set (reg:CC 21)
(compare:CC (match_dup 0) (const_int 0)))]
""
"ldi\\t%1,%0"
[(set_attr "type" "unarycc")
(set_attr "data" "int16")])
 
(define_insn "*movqi_set"
[(set (reg:CC 21)
(compare:CC (match_operand:QI 1 "src_operand" "rIm")
(const_int 0)))
(set (match_operand:QI 0 "ext_reg_operand" "=d")
(match_dup 1))]
""
"ldi\\t%1,%0"
[(set_attr "type" "unarycc")
(set_attr "data" "int16")])
 
; This pattern probably gets in the way and requires a scratch register
; when a simple compare with zero will suffice.
;(define_insn "*movqi_test"
; [(set (reg:CC 21)
; (compare:CC (match_operand:QI 1 "src_operand" "rIm")
; (const_int 0)))
; (clobber (match_scratch:QI 0 "=d"))]
; ""
; "@
; ldi\\t%1,%0"
; [(set_attr "type" "unarycc")
; (set_attr "data" "int16")])
 
; If one of the operands is not a register, then we should
; emit two insns, using a scratch register. This will produce
; better code in loops if the source operand is invariant, since
; the source reload can be optimized out. During reload we cannot
; use change_address or force_reg which will allocate new pseudo regs.
 
; Unlike most other insns, the move insns can't be split with
; different predicates, because register spilling and other parts of
; the compiler, have memoized the insn number already.
 
(define_expand "movqi"
[(set (match_operand:QI 0 "general_operand" "")
(match_operand:QI 1 "general_operand" ""))]
""
"
{
if (c4x_emit_move_sequence (operands, QImode))
DONE;
}")
 
 
; As far as GCC is concerned, the moves are performed in parallel
; thus it must be convinced that there is no aliasing.
; It also assumes that the input operands are simultaneously loaded
; and then the output operands are simultaneously stored.
; With the C4x, if there are parallel stores to the same address
; both stores are executed.
; If there is a parallel load and store to the same address,
; the load is performed first.
; The problem with this pattern is that reload can spoil
; the show when it eliminates a reference to the frame pointer.
; This can invalidate the memory addressing mode, i.e., when
; the displacement is greater than 1.
(define_insn "movqi_parallel"
[(set (match_operand:QI 0 "parallel_operand" "=q,S<>!V,q,S<>!V")
(match_operand:QI 1 "parallel_operand" "S<>!V,q,S<>!V,q"))
(set (match_operand:QI 2 "parallel_operand" "=q,S<>!V,S<>!V,q")
(match_operand:QI 3 "parallel_operand" "S<>!V,q,q,S<>!V"))]
"TARGET_PARALLEL && valid_parallel_load_store (operands, QImode)"
"@
ldi1\\t%1,%0\\n||\\tldi2\\t%3,%2
sti1\\t%1,%0\\n||\\tsti2\\t%3,%2
ldi\\t%1,%0\\n||\\tsti\\t%3,%2
ldi\\t%3,%2\\n||\\tsti\\t%1,%0"
[(set_attr "type" "load_load,store_store,load_store,store_load")])
 
;
; PUSH/POP
;
(define_insn "pushqi"
[(set (mem:QI (pre_inc:QI (reg:QI 20)))
(match_operand:QI 0 "reg_operand" "r"))]
""
"push\\t%0"
[(set_attr "type" "push")])
 
(define_insn "push_st"
[(set (mem:QI (pre_inc:QI (reg:QI 20)))
(unspec:QI [(reg:QI 21)] UNSPEC_PUSH_ST))
(use (reg:QI 21))]
""
"push\\tst"
[(set_attr "type" "push")])
 
(define_insn "push_dp"
[(set (mem:QI (pre_inc:QI (reg:QI 20)))
(unspec:QI [(reg:QI 16)] UNSPEC_PUSH_DP))
(use (reg:QI 16))]
""
"push\\tdp"
[(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")])
 
(define_insn "pop_st"
[(set (unspec:QI [(reg:QI 21)] UNSPEC_POP_ST)
(mem:QI (post_dec:QI (reg:QI 20))))
(clobber (reg:CC 21))]
""
"pop\\tst"
[(set_attr "type" "pop")])
 
(define_insn "pop_dp"
[(set (unspec:QI [(reg:QI 16)] UNSPEC_POP_DP)
(mem:QI (post_dec:QI (reg:QI 20))))
(clobber (reg:CC 16))]
""
"pop\\tdp"
[(set_attr "type" "pop")])
 
(define_insn "popqi_unspec"
[(set (unspec:QI [(match_operand:QI 0 "reg_operand" "=r")] UNSPEC_POPQI)
(mem:QI (post_dec:QI (reg:QI 20))))
(clobber (match_dup 0))
(clobber (reg:CC 21))]
""
"pop\\t%0"
[(set_attr "type" "pop")])
 
;
; ABSI
;
(define_expand "absqi2"
[(parallel [(set (match_operand:QI 0 "reg_operand" "")
(abs:QI (match_operand:QI 1 "src_operand" "")))
(clobber (reg:CC_NOOV 21))])]
""
"")
 
(define_insn "*absqi2_clobber"
[(set (match_operand:QI 0 "reg_operand" "=d,c")
(abs:QI (match_operand:QI 1 "src_operand" "rIm,rIm")))
(clobber (reg:CC_NOOV 21))]
""
"absi\\t%1,%0"
[(set_attr "type" "unarycc,unary")
(set_attr "data" "int16,int16")])
 
(define_insn "*absqi2_noclobber"
[(set (match_operand:QI 0 "std_reg_operand" "=c")
(abs:QI (match_operand:QI 1 "src_operand" "rIm")))]
""
"absi\\t%1,%0"
[(set_attr "type" "unary")
(set_attr "data" "int16")])
 
(define_split
[(set (match_operand:QI 0 "std_reg_operand" "")
(abs:QI (match_operand:QI 1 "src_operand" "")))
(clobber (reg:CC_NOOV 21))]
"reload_completed"
[(set (match_dup 0)
(abs:QI (match_dup 1)))]
"")
 
(define_insn "*absqi2_test"
[(set (reg:CC_NOOV 21)
(compare:CC_NOOV (abs:QI (match_operand:QI 1 "src_operand" "rIm"))
(const_int 0)))
(clobber (match_scratch:QI 0 "=d"))]
""
"absi\\t%1,%0"
[(set_attr "type" "unarycc")
(set_attr "data" "int16")])
 
(define_insn "*absqi2_set"
[(set (reg:CC_NOOV 21)
(compare:CC_NOOV (abs:QI (match_operand:QI 1 "src_operand" "rIm"))
(const_int 0)))
(set (match_operand:QI 0 "ext_reg_operand" "=d")
(abs:QI (match_dup 1)))]
""
"absi\\t%1,%0"
[(set_attr "type" "unarycc")
(set_attr "data" "int16")])
 
;
; NEGI
;
(define_expand "negqi2"
[(parallel [(set (match_operand:QI 0 "reg_operand" "")
(neg:QI (match_operand:QI 1 "src_operand" "")))
(clobber (reg:CC_NOOV 21))])]
""
"")
 
(define_insn "*negqi2_clobber"
[(set (match_operand:QI 0 "reg_operand" "=d,c")
(neg:QI (match_operand:QI 1 "src_operand" "rIm,rIm")))
(clobber (reg:CC_NOOV 21))]
""
"negi\\t%1,%0"
[(set_attr "type" "unarycc,unary")
(set_attr "data" "int16,int16")])
 
(define_insn "*negqi2_noclobber"
[(set (match_operand:QI 0 "std_reg_operand" "=c")
(neg:QI (match_operand:QI 1 "src_operand" "rIm")))]
""
"negi\\t%1,%0"
[(set_attr "type" "unary")
(set_attr "data" "int16")])
 
(define_split
[(set (match_operand:QI 0 "std_reg_operand" "")
(neg:QI (match_operand:QI 1 "src_operand" "")))
(clobber (reg:CC_NOOV 21))]
"reload_completed"
[(set (match_dup 0)
(neg:QI (match_dup 1)))]
"")
 
(define_insn "*negqi2_test"
[(set (reg:CC_NOOV 21)
(compare:CC_NOOV (neg:QI (match_operand:QI 1 "src_operand" "rIm"))
(const_int 0)))
(clobber (match_scratch:QI 0 "=d"))]
""
"negi\\t%1,%0"
[(set_attr "type" "unarycc")
(set_attr "data" "int16")])
 
(define_insn "*negqi2_set"
[(set (reg:CC_NOOV 21)
(compare:CC_NOOV (neg:QI (match_operand:QI 1 "src_operand" "rIm"))
(const_int 0)))
(set (match_operand:QI 0 "ext_reg_operand" "=d")
(neg:QI (match_dup 1)))]
""
"negi\\t%1,%0"
[(set_attr "type" "unarycc")
(set_attr "data" "int16")])
 
(define_insn "*negbqi2_clobber"
[(set (match_operand:QI 0 "ext_reg_operand" "=d")
(neg:QI (match_operand:QI 1 "src_operand" "rIm")))
(use (reg:CC_NOOV 21))
(clobber (reg:CC_NOOV 21))]
""
"negb\\t%1,%0"
[(set_attr "type" "unarycc")
(set_attr "data" "int16")])
 
;
; NOT
;
(define_expand "one_cmplqi2"
[(parallel [(set (match_operand:QI 0 "reg_operand" "")
(not:QI (match_operand:QI 1 "lsrc_operand" "")))
(clobber (reg:CC 21))])]
""
"")
 
(define_insn "*one_cmplqi2_clobber"
[(set (match_operand:QI 0 "reg_operand" "=d,c")
(not:QI (match_operand:QI 1 "lsrc_operand" "rLm,rLm")))
(clobber (reg:CC 21))]
""
"not\\t%1,%0"
[(set_attr "type" "unarycc,unary")
(set_attr "data" "uint16,uint16")])
 
(define_insn "*one_cmplqi2_noclobber"
[(set (match_operand:QI 0 "std_reg_operand" "=c")
(not:QI (match_operand:QI 1 "lsrc_operand" "rLm")))]
""
"not\\t%1,%0"
[(set_attr "type" "unary")
(set_attr "data" "uint16")])
 
(define_split
[(set (match_operand:QI 0 "std_reg_operand" "")
(not:QI (match_operand:QI 1 "lsrc_operand" "")))
(clobber (reg:CC 21))]
"reload_completed"
[(set (match_dup 0)
(not:QI (match_dup 1)))]
"")
 
(define_insn "*one_cmplqi2_test"
[(set (reg:CC 21)
(compare:CC (not:QI (match_operand:QI 1 "lsrc_operand" "rLm"))
(const_int 0)))
(clobber (match_scratch:QI 0 "=d"))]
""
"not\\t%1,%0"
[(set_attr "type" "unarycc")
(set_attr "data" "uint16")])
 
(define_insn "*one_cmplqi2_set"
[(set (reg:CC 21)
(compare:CC (not:QI (match_operand:QI 1 "lsrc_operand" "rLm"))
(const_int 0)))
(set (match_operand:QI 0 "ext_reg_operand" "=d")
(not:QI (match_dup 1)))]
""
"not\\t%1,%0"
[(set_attr "type" "unarycc")
(set_attr "data" "uint16")])
 
(define_insn "*one_cmplqi2_const_clobber"
[(set (match_operand:QI 0 "reg_operand" "=d,c")
(match_operand:QI 1 "not_const_operand" "N,N"))
(clobber (reg:CC 21))]
""
"@
not\\t%N1,%0
not\\t%N1,%0"
[(set_attr "type" "unarycc,unary")
(set_attr "data" "not_uint16,not_uint16")])
 
; movqi can use this for loading an integer that can't normally
; fit into a 16-bit signed integer. The drawback is that it cannot
; go into R0-R11 since that will clobber the CC and movqi shouldn't
; do that. This can cause additional reloading but in most cases
; this will cause only an additional register move. With the large
; memory model we require an extra instruction to load DP anyway,
; if we're loading the constant from memory. The big advantage of
; allowing constants that satisfy not_const_operand in movqi, is that
; it allows andn to be generated more often.
; However, there is a problem if GCC has decided that it wants
; to use R0-R11, since we won't have a matching pattern...
; In interim, we prevent immed_const allowing `N' constants.
(define_insn "*one_cmplqi2_const_noclobber"
[(set (match_operand:QI 0 "std_reg_operand" "=c")
(match_operand:QI 1 "not_const_operand" "N"))]
""
"not\\t%N1,%0"
[(set_attr "type" "unary")
(set_attr "data" "not_uint16")])
 
;
; ROL
;
(define_expand "rotlqi3"
[(parallel [(set (match_operand:QI 0 "reg_operand" "")
(rotate:QI (match_operand:QI 1 "reg_operand" "")
(match_operand:QI 2 "const_int_operand" "")))
(clobber (reg:CC 21))])]
""
"if (INTVAL (operands[2]) > 4)
FAIL; /* Open code as two shifts and an or */
if (INTVAL (operands[2]) > 1)
{
int i;
rtx tmp;
 
/* If we have 4 or fewer shifts, then it is probably faster
to emit separate ROL instructions. A C3x requires
at least 4 instructions (a C4x requires at least 3), to
perform a rotation by shifts. */
 
tmp = operands[1];
for (i = 0; i < INTVAL (operands[2]) - 1; i++)
{
tmp = gen_reg_rtx (QImode);
emit_insn (gen_rotl_1_clobber (tmp, operands[1]));
operands[1] = tmp;
}
emit_insn (gen_rotl_1_clobber (operands[0], tmp));
DONE;
}")
 
(define_insn "rotl_1_clobber"
[(set (match_operand:QI 0 "reg_operand" "=d,c")
(rotate:QI (match_operand:QI 1 "reg_operand" "0,0")
(const_int 1)))
(clobber (reg:CC 21))]
""
"rol\\t%0"
[(set_attr "type" "unarycc,unary")])
; Default to int16 data attr.
 
;
; ROR
;
(define_expand "rotrqi3"
[(parallel [(set (match_operand:QI 0 "reg_operand" "")
(rotatert:QI (match_operand:QI 1 "reg_operand" "")
(match_operand:QI 2 "const_int_operand" "")))
(clobber (reg:CC 21))])]
""
"if (INTVAL (operands[2]) > 4)
FAIL; /* Open code as two shifts and an or */
if (INTVAL (operands[2]) > 1)
{
int i;
rtx tmp;
/* If we have 4 or fewer shifts, then it is probably faster
to emit separate ROL instructions. A C3x requires
at least 4 instructions (a C4x requires at least 3), to
perform a rotation by shifts. */
tmp = operands[1];
for (i = 0; i < INTVAL (operands[2]) - 1; i++)
{
tmp = gen_reg_rtx (QImode);
emit_insn (gen_rotr_1_clobber (tmp, operands[1]));
operands[1] = tmp;
}
emit_insn (gen_rotr_1_clobber (operands[0], tmp));
DONE;
}")
 
(define_insn "rotr_1_clobber"
[(set (match_operand:QI 0 "reg_operand" "=d,c")
(rotatert:QI (match_operand:QI 1 "reg_operand" "0,0")
(const_int 1)))
(clobber (reg:CC 21))]
""
"ror\\t%0"
[(set_attr "type" "unarycc,unary")])
; Default to int16 data attr.
 
 
;
; THREE OPERAND INTEGER INSTRUCTIONS
;
 
;
; ADDI
;
; This is used by reload when it calls gen_add2_insn for address arithmetic
; so we must emit the pattern that doesn't clobber CC.
;
(define_expand "addqi3"
[(parallel [(set (match_operand:QI 0 "std_or_reg_operand" "")
(plus:QI (match_operand:QI 1 "src_operand" "")
(match_operand:QI 2 "src_operand" "")))
(clobber (reg:CC_NOOV 21))])]
""
"legitimize_operands (PLUS, operands, QImode);
if (reload_in_progress
|| (! IS_PSEUDO_REG (operands[0])
&& ! IS_EXT_REG (operands[0])))
{
emit_insn (gen_addqi3_noclobber (operands[0], operands[1], operands[2]));
DONE;
}")
 
; This pattern is required primarily for manipulating the stack pointer
; where GCC doesn't expect CC to be clobbered or for calculating
; addresses during reload. Since this is a more specific pattern
; it needs to go first (otherwise we get into problems trying to decide
; to add clobbers).
(define_insn "addqi3_noclobber"
[(set (match_operand:QI 0 "std_reg_operand" "=c,c,c")
(plus:QI (match_operand:QI 1 "src_operand" "%0,rR,rS<>")
(match_operand:QI 2 "src_operand" "rIm,JR,rS<>")))]
"valid_operands (PLUS, operands, QImode)"
"@
addi\\t%2,%0
addi3\\t%2,%1,%0
addi3\\t%2,%1,%0"
[(set_attr "type" "binary,binary,binary")])
; Default to int16 data attr.
 
(define_insn "*addqi3_clobber"
[(set (match_operand:QI 0 "reg_operand" "=d,d,?d,c,c,?c")
(plus:QI (match_operand:QI 1 "src_operand" "%0,rR,rS<>,0,rR,rS<>")
(match_operand:QI 2 "src_operand" "rIm,JR,rS<>,rIm,JR,rS<>")))
(clobber (reg:CC_NOOV 21))]
"valid_operands (PLUS, operands, QImode)"
"@
addi\\t%2,%0
addi3\\t%2,%1,%0
addi3\\t%2,%1,%0
addi\\t%2,%0
addi3\\t%2,%1,%0
addi3\\t%2,%1,%0"
[(set_attr "type" "binarycc,binarycc,binarycc,binary,binary,binary")])
; Default to int16 data attr.
 
(define_split
[(set (match_operand:QI 0 "std_reg_operand" "")
(plus:QI (match_operand:QI 1 "src_operand" "")
(match_operand:QI 2 "src_operand" "")))
(clobber (reg:CC_NOOV 21))]
"reload_completed"
[(set (match_dup 0)
(plus:QI (match_dup 1)
(match_dup 2)))]
"")
 
(define_insn "*addqi3_test"
[(set (reg:CC_NOOV 21)
(compare:CC_NOOV (plus:QI (match_operand:QI 1 "src_operand" "%0,rR,rS<>")
(match_operand:QI 2 "src_operand" "rIm,JR,rS<>"))
(const_int 0)))
(clobber (match_scratch:QI 0 "=d,d,d"))]
"valid_operands (PLUS, operands, QImode)"
"@
addi\\t%2,%0
addi3\\t%2,%1,%0
addi3\\t%2,%1,%0"
[(set_attr "type" "binarycc,binarycc,binarycc")])
; Default to int16 data attr.
 
; gcc does this in combine.c we just reverse it here
(define_insn "*cmp_neg"
[(set (reg:CC_NOOV 21)
(compare:CC_NOOV (match_operand:QI 1 "src_operand" "%0,rR,rS<>")
(neg: QI (match_operand:QI 2 "src_operand" "g,JR,rS<>"))))
(clobber (match_scratch:QI 0 "=d,d,d"))]
"valid_operands (PLUS, operands, QImode)"
"@
addi\\t%2,%0
addi3\\t%2,%1,%0
addi3\\t%2,%1,%0"
[(set_attr "type" "binarycc,binarycc,binarycc")])
(define_peephole
[(parallel [(set (match_operand:QI 0 "ext_reg_operand" "=d,d,d")
(plus:QI (match_operand:QI 1 "src_operand" "%0,rR,rS<>")
(match_operand:QI 2 "src_operand" "g,JR,rS<>")))
(clobber (reg:CC_NOOV 21))])
(set (reg:CC_NOOV 21)
(compare:CC_NOOV (match_dup 0) (const_int 0)))]
"valid_operands (PLUS, operands, QImode)"
"@
addi\\t%2,%0
addi3\\t%2,%1,%0
addi3\\t%2,%1,%0"
[(set_attr "type" "binarycc,binarycc,binarycc")])
 
(define_insn "*addqi3_set"
[(set (reg:CC_NOOV 21)
(compare:CC_NOOV (plus:QI (match_operand:QI 1 "src_operand" "%0,rR,rS<>")
(match_operand:QI 2 "src_operand" "rIm,JR,rS<>"))
(const_int 0)))
(set (match_operand:QI 0 "ext_reg_operand" "=d,d,d")
(plus:QI (match_dup 1) (match_dup 2)))]
"valid_operands (PLUS, operands, QImode)"
"@
addi\\t%2,%0
addi3\\t%2,%1,%0
addi3\\t%2,%1,%0"
[(set_attr "type" "binarycc,binarycc,binarycc")])
; Default to int16 data attr.
 
 
; This pattern is required during reload when eliminate_regs_in_insn
; effectively converts a move insn into an add insn when the src
; operand is the frame pointer plus a constant. Without this
; pattern, gen_addqi3 can be called with a register for operand0
; that can clobber CC.
; For example, we may have (set (mem (reg ar0)) (reg 99))
; with (set (reg 99) (plus (reg ar3) (const_int 8)))
; Now since ar3, the frame pointer, is unchanging within the function,
; (plus (reg ar3) (const_int 8)) is considered a constant.
; eliminate_regs_in_insn substitutes this constant to give
; (set (mem (reg ar0)) (plus (reg ar3) (const_int 8))).
; This is an invalid C4x insn but if we don't provide a pattern
; for it, it will be considered to be a move insn for reloading.
(define_insn "*addqi3_noclobber_reload"
[(set (match_operand:QI 0 "std_reg_operand" "=c,c,c")
(plus:QI (match_operand:QI 1 "src_operand" "%0,rR,rS<>")
(match_operand:QI 2 "src_operand" "rIm,JR,rS<>")))]
"reload_in_progress"
"@
addi\\t%2,%0
addi3\\t%2,%1,%0
addi3\\t%2,%1,%0"
[(set_attr "type" "binary,binary,binary")])
; Default to int16 data attr.
 
 
(define_insn "*addqi3_carry_clobber"
[(set (match_operand:QI 0 "reg_operand" "=d,d,?d,c,c,?c")
(plus:QI (match_operand:QI 1 "src_operand" "%0,rR,rS<>,0,rR,rS<>")
(match_operand:QI 2 "src_operand" "rIm,JR,rS<>,rIm,JR,rS<>")))
(use (reg:CC_NOOV 21))
(clobber (reg:CC_NOOV 21))]
"valid_operands (PLUS, operands, QImode)"
"@
addc\\t%2,%0
addc3\\t%2,%1,%0
addc3\\t%2,%1,%0
addc\\t%2,%0
addc3\\t%2,%1,%0
addc3\\t%2,%1,%0"
[(set_attr "type" "binarycc,binarycc,binarycc,binary,binary,binary")])
; Default to int16 data attr.
 
 
;
; SUBI/SUBRI
;
(define_expand "subqi3"
[(parallel [(set (match_operand:QI 0 "reg_operand" "")
(minus:QI (match_operand:QI 1 "src_operand" "")
(match_operand:QI 2 "src_operand" "")))
(clobber (reg:CC_NOOV 21))])]
""
"legitimize_operands (MINUS, operands, QImode);")
 
(define_insn "*subqi3_clobber"
[(set (match_operand:QI 0 "reg_operand" "=d,d,d,?d,c,c,c,?c")
(minus:QI (match_operand:QI 1 "src_operand" "0,rIm,rR,rS<>,0,rIm,rR,rS<>")
(match_operand:QI 2 "src_operand" "rIm,0,JR,rS<>,rIm,0,JR,rS<>")))
(clobber (reg:CC_NOOV 21))]
"valid_operands (MINUS, operands, QImode)"
"@
subi\\t%2,%0
subri\\t%1,%0
subi3\\t%2,%1,%0
subi3\\t%2,%1,%0
subi\\t%2,%0
subri\\t%1,%0
subi3\\t%2,%1,%0
subi3\\t%2,%1,%0"
[(set_attr "type" "binarycc,binarycc,binarycc,binarycc,binary,binary,binary,binary")])
; Default to int16 data attr.
 
(define_split
[(set (match_operand:QI 0 "std_reg_operand" "")
(minus:QI (match_operand:QI 1 "src_operand" "")
(match_operand:QI 2 "src_operand" "")))
(clobber (reg:CC_NOOV 21))]
"reload_completed"
[(set (match_dup 0)
(minus:QI (match_dup 1)
(match_dup 2)))]
"")
 
(define_insn "*subqi3_test"
[(set (reg:CC_NOOV 21)
(compare:CC_NOOV (minus:QI (match_operand:QI 1 "src_operand" "0,rIm,rR,rS<>")
(match_operand:QI 2 "src_operand" "rIm,0,JR,rS<>"))
(const_int 0)))
(clobber (match_scratch:QI 0 "=d,d,d,?d"))]
"valid_operands (MINUS, operands, QImode)"
"@
subi\\t%2,%0
subri\\t%1,%0
subi3\\t%2,%1,%0
subi3\\t%2,%1,%0"
[(set_attr "type" "binarycc,binarycc,binarycc,binarycc")])
; Default to int16 data attr.
 
(define_peephole
[(parallel [(set (match_operand:QI 0 "ext_reg_operand" "=d,d,d,?d")
(minus:QI (match_operand:QI 1 "src_operand" "0,rIm,rR,rS<>")
(match_operand:QI 2 "src_operand" "rIm,0,JR,rS<>")))
(clobber (reg:CC_NOOV 21))])
(set (reg:CC_NOOV 21)
(compare:CC_NOOV (match_dup 0) (const_int 0)))]
"valid_operands (MINUS, operands, QImode)"
"@
subi\\t%2,%0
subri\\t%1,%0
subi3\\t%2,%1,%0
subi3\\t%2,%1,%0"
[(set_attr "type" "binarycc,binarycc,binarycc,binarycc")])
(define_insn "*subqi3_set"
[(set (reg:CC_NOOV 21)
(compare:CC_NOOV (minus:QI (match_operand:QI 1 "src_operand" "0,rIm,rR,rS<>")
(match_operand:QI 2 "src_operand" "rIm,0,JR,rS<>"))
(const_int 0)))
(set (match_operand:QI 0 "ext_reg_operand" "=d,d,d,?d")
(minus:QI (match_dup 1)
(match_dup 2)))]
"valid_operands (MINUS, operands, QImode)"
"@
subi\\t%2,%0
subri\\t%1,%0
subi3\\t%2,%1,%0
subi3\\t%2,%1,%0"
[(set_attr "type" "binarycc,binarycc,binarycc,binarycc")])
; Default to int16 data attr.
 
(define_insn "*subqi3_noclobber"
[(set (match_operand:QI 0 "std_reg_operand" "=c,c,c,?c")
(minus:QI (match_operand:QI 1 "src_operand" "0,rIm,rR,rS<>")
(match_operand:QI 2 "src_operand" "rIm,0,JR,rS<>")))]
"valid_operands (MINUS, operands, QImode)"
"@
subi\\t%2,%0
subri\\t%1,%0
subi3\\t%2,%1,%0
subi3\\t%2,%1,%0"
[(set_attr "type" "binary,binary,binary,binary")])
; Default to int16 data attr.
 
(define_insn "*subqi3_carry_clobber"
[(set (match_operand:QI 0 "reg_operand" "=d,d,d,?d,c,c,c,?c")
(minus:QI (match_operand:QI 1 "src_operand" "0,rIm,rR,rS<>,0,rIm,rR,rS<>")
(match_operand:QI 2 "src_operand" "rIm,0,JR,rS<>,rIm,0,JR,rS<>")))
(use (reg:CC_NOOV 21))
(clobber (reg:CC_NOOV 21))]
"valid_operands (MINUS, operands, QImode)"
"@
subb\\t%2,%0
subrb\\t%1,%0
subb3\\t%2,%1,%0
subb3\\t%2,%1,%0
subb\\t%2,%0
subrb\\t%1,%0
subb3\\t%2,%1,%0
subb3\\t%2,%1,%0"
[(set_attr "type" "binarycc,binarycc,binarycc,binarycc,binary,binary,binary,binary")])
; Default to int16 data attr.
 
(define_insn "*subqi3_carry_set"
[(set (reg:CC_NOOV 21)
(compare:CC_NOOV (minus:QI (match_operand:QI 1 "src_operand" "0,rIm,rR,rS<>")
(match_operand:QI 2 "src_operand" "rIm,0,JR,rS<>"))
(const_int 0)))
(set (match_operand:QI 0 "ext_reg_operand" "=d,d,d,?d")
(minus:QI (match_dup 1)
(match_dup 2)))
(use (reg:CC_NOOV 21))]
"valid_operands (MINUS, operands, QImode)"
"@
subb\\t%2,%0
subrb\\t%1,%0
subb3\\t%2,%1,%0
subb3\\t%2,%1,%0"
[(set_attr "type" "binarycc,binarycc,binarycc,binarycc")])
; Default to int16 data attr.
 
;
; MPYI
;
(define_expand "mulqi3"
[(parallel [(set (match_operand:QI 0 "reg_operand" "")
(mult:QI (match_operand:QI 1 "src_operand" "")
(match_operand:QI 2 "src_operand" "")))
(clobber (reg:CC_NOOV 21))])]
""
"if (TARGET_MPYI || (GET_CODE (operands[2]) == CONST_INT
&& exact_log2 (INTVAL (operands[2])) >= 0))
legitimize_operands (MULT, operands, QImode);
else
{
if (GET_CODE (operands[2]) == CONST_INT)
{
/* Let GCC try to synthesize 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 (smul_optab->handlers[(int) QImode].libfunc,
MULT, QImode, operands);
DONE;
}
")
 
(define_insn "*mulqi3_clobber"
[(set (match_operand:QI 0 "reg_operand" "=d,d,?d,c,c,?c")
(mult:QI (match_operand:QI 1 "src_operand" "%0,rR,rS<>,0,rR,rS<>")
(match_operand:QI 2 "src_operand" "rIm,JR,rS<>,rIm,JR,rS<>")))
(clobber (reg:CC_NOOV 21))]
"valid_operands (MULT, operands, QImode)"
"*
if (which_alternative == 0 || which_alternative == 3)
{
if (TARGET_C3X
&& GET_CODE (operands[2]) == CONST_INT
&& exact_log2 (INTVAL (operands[2])) >= 0)
return \"ash\\t%L2,%0\";
else
return \"mpyi\\t%2,%0\";
}
else
return \"mpyi3\\t%2,%1,%0\";"
[(set_attr "type" "binarycc,binarycc,binarycc,binary,binary,binary")])
; Default to int16 data attr.
 
(define_insn "*mulqi3_test"
[(set (reg:CC_NOOV 21)
(compare:CC_NOOV (mult:QI (match_operand:QI 1 "src_operand" "%0,rR,rS<>")
(match_operand:QI 2 "src_operand" "rIm,JR,rS<>"))
(const_int 0)))
(clobber (match_scratch:QI 0 "=d,d,d"))]
"valid_operands (MULT, operands, QImode)"
"*
if (which_alternative == 0)
{
if (TARGET_C3X
&& GET_CODE (operands[2]) == CONST_INT
&& exact_log2 (INTVAL (operands[2])) >= 0)
return \"ash\\t%L2,%0\";
else
return \"mpyi\\t%2,%0\";
}
else
return \"mpyi3\\t%2,%1,%0\";"
[(set_attr "type" "binarycc,binarycc,binarycc")])
; Default to int16 data attr.
 
(define_insn "*mulqi3_set"
[(set (reg:CC_NOOV 21)
(compare:CC_NOOV (mult:QI (match_operand:QI 1 "src_operand" "%0,rR,rS<>")
(match_operand:QI 2 "src_operand" "rIm,JR,rS<>"))
(const_int 0)))
(set (match_operand:QI 0 "ext_reg_operand" "=d,d,d")
(mult:QI (match_dup 1)
(match_dup 2)))]
"valid_operands (MULT, operands, QImode)"
"*
if (which_alternative == 0)
{
if (TARGET_C3X
&& GET_CODE (operands[2]) == CONST_INT
&& exact_log2 (INTVAL (operands[2])) >= 0)
return \"ash\\t%L2,%0\";
else
return \"mpyi\\t%2,%0\";
}
else
return \"mpyi3\\t%2,%1,%0\";"
[(set_attr "type" "binarycc,binarycc,binarycc")])
; Default to int16 data attr.
 
; The C3x multiply instruction assumes 24-bit signed integer operands
; and the 48-bit result is truncated to 32-bits.
(define_insn "mulqi3_24_clobber"
[(set (match_operand:QI 0 "reg_operand" "=d,d,?d,c,c,?c")
(mult:QI
(sign_extend:QI
(and:QI (match_operand:QI 1 "src_operand" "%0,rR,rS<>,0,rR,rS<>")
(const_int 16777215)))
(sign_extend:QI
(and:QI (match_operand:QI 2 "src_operand" "rIm,JR,rS<>,rIm,JR,rS<>")
(const_int 16777215)))))
(clobber (reg:CC_NOOV 21))]
"TARGET_C3X && valid_operands (MULT, operands, QImode)"
"@
mpyi\\t%2,%0
mpyi3\\t%2,%1,%0
mpyi3\\t%2,%1,%0
mpyi\\t%2,%0
mpyi3\\t%2,%1,%0
mpyi3\\t%2,%1,%0"
[(set_attr "type" "binarycc,binarycc,binarycc,binary,binary,binary")])
; Default to int16 data attr.
 
 
; Fast square function for C3x where TARGET_MPYI not asserted
(define_expand "sqrqi2_inline"
[(set (match_dup 7) (match_operand:QI 1 "src_operand" ""))
(parallel [(set (match_dup 3)
(lshiftrt:QI (match_dup 7) (const_int 16)))
(clobber (reg:CC 21))])
(parallel [(set (match_dup 2)
(and:QI (match_dup 7) (const_int 65535)))
(clobber (reg:CC 21))])
(parallel [(set (match_dup 4)
(mult:QI (sign_extend:QI (and:QI (match_dup 2)
(const_int 16777215)))
(sign_extend:QI (and:QI (match_dup 2)
(const_int 16777215)))))
(clobber (reg:CC_NOOV 21))])
(parallel [(set (match_dup 5)
(mult:QI (sign_extend:QI (and:QI (match_dup 2)
(const_int 16777215)))
(sign_extend:QI (and:QI (match_dup 3)
(const_int 16777215)))))
(clobber (reg:CC_NOOV 21))])
(parallel [(set (match_dup 6)
(ashift:QI (match_dup 5) (const_int 17)))
(clobber (reg:CC 21))])
(parallel [(set (match_operand:QI 0 "reg_operand" "")
(plus:QI (match_dup 4) (match_dup 6)))
(clobber (reg:CC_NOOV 21))])]
""
"
operands[2] = gen_reg_rtx (QImode); /* a = val & 0xffff */
operands[3] = gen_reg_rtx (QImode); /* b = val >> 16 */
operands[4] = gen_reg_rtx (QImode); /* a * a */
operands[5] = gen_reg_rtx (QImode); /* a * b */
operands[6] = gen_reg_rtx (QImode); /* (a * b) << 17 */
operands[7] = gen_reg_rtx (QImode); /* val */
")
 
; Inlined integer multiply for C3x
(define_expand "mulqi3_inline"
[(set (match_dup 12) (const_int -16))
(set (match_dup 13) (match_operand:QI 1 "src_operand" ""))
(set (match_dup 14) (match_operand:QI 2 "src_operand" ""))
(parallel [(set (match_dup 4)
(lshiftrt:QI (match_dup 13) (neg:QI (match_dup 12))))
(clobber (reg:CC 21))])
(parallel [(set (match_dup 6)
(lshiftrt:QI (match_dup 14) (neg:QI (match_dup 12))))
(clobber (reg:CC 21))])
(parallel [(set (match_dup 3)
(and:QI (match_dup 13)
(const_int 65535)))
(clobber (reg:CC 21))])
(parallel [(set (match_dup 5)
(and:QI (match_dup 14)
(const_int 65535)))
(clobber (reg:CC 21))])
(parallel [(set (match_dup 7)
(mult:QI (sign_extend:QI (and:QI (match_dup 4)
(const_int 16777215)))
(sign_extend:QI (and:QI (match_dup 5)
(const_int 16777215)))))
(clobber (reg:CC_NOOV 21))])
(parallel [(set (match_dup 8)
(mult:QI (sign_extend:QI (and:QI (match_dup 3)
(const_int 16777215)))
(sign_extend:QI (and:QI (match_dup 5)
(const_int 16777215)))))
(clobber (reg:CC_NOOV 21))])
(parallel [(set (match_dup 9)
(mult:QI (sign_extend:QI (and:QI (match_dup 3)
(const_int 16777215)))
(sign_extend:QI (and:QI (match_dup 6)
(const_int 16777215)))))
(clobber (reg:CC_NOOV 21))])
(parallel [(set (match_dup 10)
(plus:QI (match_dup 7) (match_dup 9)))
(clobber (reg:CC_NOOV 21))])
(parallel [(set (match_dup 11)
(ashift:QI (match_dup 10) (const_int 16)))
(clobber (reg:CC 21))])
(parallel [(set (match_operand:QI 0 "reg_operand" "")
(plus:QI (match_dup 8) (match_dup 11)))
(clobber (reg:CC_NOOV 21))])]
"TARGET_C3X"
"
operands[3] = gen_reg_rtx (QImode); /* a = arg1 & 0xffff */
operands[4] = gen_reg_rtx (QImode); /* b = arg1 >> 16 */
operands[5] = gen_reg_rtx (QImode); /* a = arg2 & 0xffff */
operands[6] = gen_reg_rtx (QImode); /* b = arg2 >> 16 */
operands[7] = gen_reg_rtx (QImode); /* b * c */
operands[8] = gen_reg_rtx (QImode); /* a * c */
operands[9] = gen_reg_rtx (QImode); /* a * d */
operands[10] = gen_reg_rtx (QImode); /* b * c + a * d */
operands[11] = gen_reg_rtx (QImode); /* (b *c + a * d) << 16 */
operands[12] = gen_reg_rtx (QImode); /* -16 */
operands[13] = gen_reg_rtx (QImode); /* arg1 */
operands[14] = gen_reg_rtx (QImode); /* arg2 */
")
 
;
; MPYSHI (C4x only)
;
(define_expand "smulqi3_highpart"
[(parallel [(set (match_operand:QI 0 "reg_operand" "")
(truncate:QI
(lshiftrt:HI
(mult:HI
(sign_extend:HI (match_operand:QI 1 "src_operand" ""))
(sign_extend:HI (match_operand:QI 2 "src_operand" "")))
(const_int 32))))
(clobber (reg:CC_NOOV 21))])]
""
"legitimize_operands (MULT, operands, QImode);
if (TARGET_C3X)
{
c4x_emit_libcall_mulhi (smulhi3_libfunc, SIGN_EXTEND, QImode, operands);
DONE;
}
")
 
(define_insn "*smulqi3_highpart_clobber"
[(set (match_operand:QI 0 "reg_operand" "=d,d,?d,c,c,?c")
(truncate:QI
(lshiftrt:HI
(mult:HI
(sign_extend:HI (match_operand:QI 1 "src_operand" "%0,rR,rS<>,0,rR,rS<>"))
(sign_extend:HI (match_operand:QI 2 "src_operand" "rIm,JR,rS<>,rIm,JR,rS<>")))
(const_int 32))))
(clobber (reg:CC_NOOV 21))]
"! TARGET_C3X && valid_operands (MULT, operands, QImode)"
"@
mpyshi\\t%2,%0
mpyshi3\\t%2,%1,%0
mpyshi3\\t%2,%1,%0
mpyshi\\t%2,%0
mpyshi3\\t%2,%1,%0
mpyshi3\\t%2,%1,%0"
[(set_attr "type" "binarycc,binarycc,binarycc,binary,binary,binary")
(set_attr "data" "int16,int16,int16,int16,int16,int16")])
 
(define_insn "*smulqi3_highpart_noclobber"
[(set (match_operand:QI 0 "std_reg_operand" "=c,c,?c")
(truncate:QI
(lshiftrt:HI
(mult:HI
(sign_extend:HI (match_operand:QI 1 "src_operand" "0,rR,rS<>"))
(sign_extend:HI (match_operand:QI 2 "src_operand" "rIm,JR,rS<>")))
(const_int 32))))]
"! TARGET_C3X && valid_operands (MULT, operands, QImode)"
"@
mpyshi\\t%2,%0
mpyshi3\\t%2,%1,%0
mpyshi3\\t%2,%1,%0"
[(set_attr "type" "binary,binary,binary")
(set_attr "data" "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
"nonimmediate_src_operand" ""))
(zero_extend:HI (match_operand:QI 2
"nonimmediate_lsrc_operand" "")))
(const_int 32))))
(clobber (reg:CC_NOOV 21))])]
""
"legitimize_operands (MULT, operands, QImode);
if (TARGET_C3X)
{
c4x_emit_libcall_mulhi (umulhi3_libfunc, 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
"nonimmediate_src_operand" "%0,rR,rS<>,0,rR,rS<>"))
(zero_extend:HI (match_operand:QI 2
"nonimmediate_lsrc_operand" "rm,R,rS<>,rm,R,rS<>")))
(const_int 32))))
(clobber (reg:CC_NOOV 21))]
"! TARGET_C3X && valid_operands (MULT, operands, QImode)"
"@
mpyuhi\\t%2,%0
mpyuhi3\\t%2,%1,%0
mpyuhi3\\t%2,%1,%0
mpyuhi\\t%2,%0
mpyuhi3\\t%2,%1,%0
mpyuhi3\\t%2,%1,%0"
[(set_attr "type" "binarycc,binarycc,binarycc,binary,binary,binary")
(set_attr "data" "uint16,uint16,uint16,uint16,uint16,uint16")])
 
(define_insn "*umulqi3_highpart_noclobber"
[(set (match_operand:QI 0 "std_reg_operand" "=c,c,?c")
(truncate:QI
(lshiftrt:HI
(mult:HI
(zero_extend:HI (match_operand:QI 1
"nonimmediate_src_operand" "0,rR,rS<>"))
(zero_extend:HI (match_operand:QI 2
"nonimmediate_lsrc_operand" "rm,R,rS<>")))
(const_int 32))))]
"! TARGET_C3X && valid_operands (MULT, operands, QImode)"
"@
mpyuhi\\t%2,%0
mpyuhi3\\t%2,%1,%0
mpyuhi3\\t%2,%1,%0"
[(set_attr "type" "binary,binary,binary")
(set_attr "data" "uint16,uint16,uint16")])
 
;
; AND
;
(define_expand "andqi3"
[(parallel [(set (match_operand:QI 0 "reg_operand" "")
(and:QI (match_operand:QI 1 "src_operand" "")
(match_operand:QI 2 "tsrc_operand" "")))
(clobber (reg:CC 21))])]
""
"legitimize_operands (AND, operands, QImode);")
 
 
(define_insn "*andqi3_255_clobber"
[(set (match_operand:QI 0 "reg_operand" "=d,c")
(and:QI (match_operand:QI 1 "src_operand" "mr,mr")
(const_int 255)))
(clobber (reg:CC 21))]
"! TARGET_C3X"
"lbu0\\t%1,%0"
[(set_attr "type" "unarycc,unary")])
 
(define_insn "*andqi3_255_noclobber"
[(set (match_operand:QI 0 "reg_operand" "=c")
(and:QI (match_operand:QI 1 "src_operand" "mr")
(const_int 255)))]
"! TARGET_C3X"
"lbu0\\t%1,%0"
[(set_attr "type" "unary")])
 
 
(define_insn "*andqi3_65535_clobber"
[(set (match_operand:QI 0 "reg_operand" "=d,c")
(and:QI (match_operand:QI 1 "src_operand" "mr,mr")
(const_int 65535)))
(clobber (reg:CC 21))]
"! TARGET_C3X"
"lhu0\\t%1,%0"
[(set_attr "type" "unarycc,unary")])
 
(define_insn "*andqi3_65535_noclobber"
[(set (match_operand:QI 0 "reg_operand" "=c")
(and:QI (match_operand:QI 1 "src_operand" "mr")
(const_int 65535)))]
"! TARGET_C3X"
"lhu0\\t%1,%0"
[(set_attr "type" "unary")])
 
(define_insn "*andqi3_clobber"
[(set (match_operand:QI 0 "reg_operand" "=d,d,d,?d,c,c,c,?c")
(and:QI (match_operand:QI 1 "src_operand" "%0,0,rR,rS<>,0,0,rR,rS<>")
(match_operand:QI 2 "tsrc_operand" "N,rLm,JR,rS<>,N,rLm,JR,rS<>")))
(clobber (reg:CC 21))]
"valid_operands (AND, operands, QImode)"
"@
andn\\t%N2,%0
and\\t%2,%0
and3\\t%2,%1,%0
and3\\t%2,%1,%0
andn\\t%N2,%0
and\\t%2,%0
and3\\t%2,%1,%0
and3\\t%2,%1,%0"
[(set_attr "type" "binarycc,binarycc,binarycc,binarycc,binary,binary,binary,binary")
(set_attr "data" "not_uint16,uint16,int16,uint16,not_uint16,uint16,int16,uint16")])
 
(define_insn "*andqi3_noclobber"
[(set (match_operand:QI 0 "std_reg_operand" "=c,c,c,?c")
(and:QI (match_operand:QI 1 "src_operand" "%0,0,rR,rS<>")
(match_operand:QI 2 "tsrc_operand" "N,rLm,JR,rS<>")))]
"valid_operands (AND, operands, QImode)"
"@
andn\\t%N2,%0
and\\t%2,%0
and3\\t%2,%1,%0
and3\\t%2,%1,%0"
[(set_attr "type" "binary,binary,binary,binary")
(set_attr "data" "not_uint16,uint16,int16,uint16")])
 
(define_insn "andn_st"
[(set (unspec:QI [(reg:QI 21)] 20)
(and:QI (unspec:QI [(reg:QI 21)] UNSPEC_ANDN_ST)
(match_operand:QI 0 "" "N")))
(use (match_dup 0))
(use (reg:CC 21))
(clobber (reg:CC 21))]
""
"andn\\t%N0,st"
[(set_attr "type" "misc")
(set_attr "data" "not_uint16")])
 
(define_split
[(set (match_operand:QI 0 "std_reg_operand" "")
(and:QI (match_operand:QI 1 "src_operand" "")
(match_operand:QI 2 "tsrc_operand" "")))
(clobber (reg:CC 21))]
"reload_completed"
[(set (match_dup 0)
(and:QI (match_dup 1)
(match_dup 2)))]
"")
 
(define_insn "*andqi3_test"
[(set (reg:CC 21)
(compare:CC (and:QI (match_operand:QI 1 "src_operand" "%0,r,rR,rS<>")
(match_operand:QI 2 "tsrc_operand" "N,rLm,JR,rS<>"))
(const_int 0)))
(clobber (match_scratch:QI 0 "=d,X,X,?X"))]
"valid_operands (AND, operands, QImode)"
"@
andn\\t%N2,%0
tstb\\t%2,%1
tstb3\\t%2,%1
tstb3\\t%2,%1"
[(set_attr "type" "binarycc,binarycc,binarycc,binarycc")
(set_attr "data" "not_uint16,uint16,int16,uint16")])
 
(define_peephole
[(parallel [(set (match_operand:QI 0 "ext_reg_operand" "=d,d,d,?d")
(and:QI (match_operand:QI 1 "src_operand" "%0,0,rR,rS<>")
(match_operand:QI 2 "tsrc_operand" "N,rLm,JR,rS<>")))
(clobber (reg:CC 21))])
(set (reg:CC 21)
(compare:CC (match_dup 0) (const_int 0)))]
"valid_operands (AND, operands, QImode)"
"@
andn\\t%N2,%0
and\\t%2,%0
and3\\t%2,%1,%0
and3\\t%2,%1,%0"
[(set_attr "type" "binarycc,binarycc,binarycc,binarycc")
(set_attr "data" "not_uint16,uint16,int16,uint16")])
(define_insn "*andqi3_set"
[(set (reg:CC 21)
(compare:CC (and:QI (match_operand:QI 1 "src_operand" "%0,0,rR,rS<>")
(match_operand:QI 2 "tsrc_operand" "N,rLm,JR,rS<>"))
(const_int 0)))
(set (match_operand:QI 0 "ext_reg_operand" "=d,d,d,?d")
(and:QI (match_dup 1)
(match_dup 2)))]
"valid_operands (AND, operands, QImode)"
"@
andn\\t%N2,%0
and\\t%2,%0
and3\\t%2,%1,%0
and3\\t%2,%1,%0"
[(set_attr "type" "binarycc,binarycc,binarycc,binarycc")
(set_attr "data" "not_uint16,uint16,int16,uint16")])
 
;
; ANDN
;
; NB, this insn doesn't have commutative operands, but valid_operands
; assumes that the code AND does. We might have to kludge this if
; we make valid_operands stricter.
(define_insn "*andnqi3_clobber"
[(set (match_operand:QI 0 "reg_operand" "=d,d,?d,c,c,?c")
(and:QI (not:QI (match_operand:QI 2 "lsrc_operand" "rLm,JR,rS<>,rLm,JR,rS<>"))
(match_operand:QI 1 "src_operand" "0,rR,rS<>,0,rR,rS<>")))
(clobber (reg:CC 21))]
"valid_operands (AND, operands, QImode)"
"@
andn\\t%2,%0
andn3\\t%2,%1,%0
andn3\\t%2,%1,%0
andn\\t%2,%0
andn3\\t%2,%1,%0
andn3\\t%2,%1,%0"
[(set_attr "type" "binarycc,binarycc,binarycc,binary,binary,binary")
(set_attr "data" "uint16,int16,uint16,uint16,int16,uint16")])
 
(define_insn "*andnqi3_noclobber"
[(set (match_operand:QI 0 "std_reg_operand" "=c,c,?c")
(and:QI (not:QI (match_operand:QI 2 "lsrc_operand" "rLm,JR,rS<>"))
(match_operand:QI 1 "src_operand" "0,rR,rS<>")))]
"valid_operands (AND, operands, QImode)"
"@
andn\\t%2,%0
andn3\\t%2,%1,%0
andn3\\t%2,%1,%0"
[(set_attr "type" "binary,binary,binary")
(set_attr "data" "uint16,int16,uint16")])
 
(define_split
[(set (match_operand:QI 0 "std_reg_operand" "")
(and:QI (not:QI (match_operand:QI 2 "lsrc_operand" ""))
(match_operand:QI 1 "src_operand" "")))
(clobber (reg:CC 21))]
"reload_completed"
[(set (match_dup 0)
(and:QI (not:QI (match_dup 2))
(match_dup 1)))]
"")
 
(define_insn "*andnqi3_test"
[(set (reg:CC 21)
(compare:CC (and:QI (not:QI (match_operand:QI 2 "lsrc_operand" "rLm,JR,rS<>"))
(match_operand:QI 1 "src_operand" "0,rR,rS<>"))
(const_int 0)))
(clobber (match_scratch:QI 0 "=d,d,d"))]
"valid_operands (AND, operands, QImode)"
"@
andn\\t%2,%0
andn3\\t%2,%1,%0
andn3\\t%2,%1,%0"
[(set_attr "type" "binarycc,binarycc,binarycc")
(set_attr "data" "uint16,int16,uint16")])
 
(define_insn "*andnqi3_set"
[(set (reg:CC 21)
(compare:CC (and:QI (not:QI (match_operand:QI 2 "lsrc_operand" "rLm,JR,rS<>"))
(match_operand:QI 1 "src_operand" "0,rR,rS<>"))
(const_int 0)))
(set (match_operand:QI 0 "ext_reg_operand" "=d,d,d")
(and:QI (not:QI (match_dup 2))
(match_dup 1)))]
"valid_operands (AND, operands, QImode)"
"@
andn\\t%2,%0
andn3\\t%2,%1,%0
andn3\\t%2,%1,%0"
[(set_attr "type" "binarycc,binarycc,binarycc")
(set_attr "data" "uint16,int16,uint16")])
 
;
; OR
;
(define_expand "iorqi3"
[(parallel [(set (match_operand:QI 0 "reg_operand" "")
(ior:QI (match_operand:QI 1 "src_operand" "")
(match_operand:QI 2 "lsrc_operand" "")))
(clobber (reg:CC 21))])]
""
"legitimize_operands (IOR, operands, QImode);")
 
(define_insn "*iorqi3_clobber"
[(set (match_operand:QI 0 "reg_operand" "=d,d,?d,c,c,?c")
(ior:QI (match_operand:QI 1 "src_operand" "%0,rR,rS<>,0,rR,rS<>")
(match_operand:QI 2 "lsrc_operand" "rLm,JR,rS<>,rLm,JR,rS<>")))
(clobber (reg:CC 21))]
"valid_operands (IOR, operands, QImode)"
"@
or\\t%2,%0
or3\\t%2,%1,%0
or3\\t%2,%1,%0
or\\t%2,%0
or3\\t%2,%1,%0
or3\\t%2,%1,%0"
[(set_attr "type" "binarycc,binarycc,binarycc,binary,binary,binary")
(set_attr "data" "uint16,int16,uint16,uint16,int16,uint16")])
 
(define_split
[(set (match_operand:QI 0 "std_reg_operand" "")
(ior:QI (match_operand:QI 1 "src_operand" "")
(match_operand:QI 2 "lsrc_operand" "")))
(clobber (reg:CC 21))]
"reload_completed"
[(set (match_dup 0)
(ior:QI (match_dup 1)
(match_dup 2)))]
"")
 
(define_insn "*iorqi3_test"
[(set (reg:CC 21)
(compare:CC (ior:QI (match_operand:QI 1 "src_operand" "%0,rR,rS<>")
(match_operand:QI 2 "lsrc_operand" "rLm,JR,rS<>"))
(const_int 0)))
(clobber (match_scratch:QI 0 "=d,d,d"))]
"valid_operands (IOR, operands, QImode)"
"@
or\\t%2,%0
or3\\t%2,%1,%0
or3\\t%2,%1,%0"
[(set_attr "type" "binarycc,binarycc,binarycc")
(set_attr "data" "uint16,int16,uint16")])
 
(define_peephole
[(parallel [(set (match_operand:QI 0 "ext_reg_operand" "=d,d,d")
(ior:QI (match_operand:QI 1 "src_operand" "%0,rR,rS<>")
(match_operand:QI 2 "lsrc_operand" "rLm,JR,rS<>")))
(clobber (reg:CC 21))])
(set (reg:CC 21)
(compare:CC (match_dup 0) (const_int 0)))]
"valid_operands (IOR, operands, QImode)"
"@
or\\t%2,%0
or3\\t%2,%1,%0
or3\\t%2,%1,%0"
[(set_attr "type" "binarycc,binarycc,binarycc")
(set_attr "data" "uint16,int16,uint16")])
(define_insn "*iorqi3_set"
[(set (reg:CC 21)
(compare:CC (ior:QI (match_operand:QI 1 "src_operand" "%0,rR,rS<>")
(match_operand:QI 2 "lsrc_operand" "rLm,JR,rS<>"))
(const_int 0)))
(set (match_operand:QI 0 "ext_reg_operand" "=d,d,d")
(ior:QI (match_dup 1)
(match_dup 2)))]
"valid_operands (IOR, operands, QImode)"
"@
or\\t%2,%0
or3\\t%2,%1,%0
or3\\t%2,%1,%0"
[(set_attr "type" "binarycc,binarycc,binarycc")
(set_attr "data" "uint16,int16,uint16")])
 
; This pattern is used for loading symbol references in several parts.
(define_insn "iorqi3_noclobber"
[(set (match_operand:QI 0 "std_reg_operand" "=c,c,c")
(ior:QI (match_operand:QI 1 "src_operand" "%0,rR,rS<>")
(match_operand:QI 2 "lsrc_operand" "rLm,JR,rS<>")))]
"valid_operands (IOR, operands, QImode)"
"@
or\\t%2,%0
or3\\t%2,%1,%0
or3\\t%2,%1,%0"
[(set_attr "type" "binary,binary,binary")
(set_attr "data" "uint16,int16,uint16")])
 
;
; XOR
;
(define_expand "xorqi3"
[(parallel [(set (match_operand:QI 0 "reg_operand" "")
(xor:QI (match_operand:QI 1 "src_operand" "")
(match_operand:QI 2 "lsrc_operand" "")))
(clobber (reg:CC 21))])]
""
"legitimize_operands (XOR, operands, QImode);")
 
(define_insn "*xorqi3_clobber"
[(set (match_operand:QI 0 "reg_operand" "=d,d,?d,c,c,?c")
(xor:QI (match_operand:QI 1 "src_operand" "%0,rR,rS<>,0,rR,rS<>")
(match_operand:QI 2 "lsrc_operand" "rLm,JR,rS<>,rLm,JR,rS<>")))
(clobber (reg:CC 21))]
"valid_operands (XOR, operands, QImode)"
"@
xor\\t%2,%0
xor3\\t%2,%1,%0
xor3\\t%2,%1,%0
xor\\t%2,%0
xor3\\t%2,%1,%0
xor3\\t%2,%1,%0"
[(set_attr "type" "binarycc,binarycc,binarycc,binary,binary,binary")
(set_attr "data" "uint16,int16,uint16,uint16,int16,uint16")])
 
(define_insn "*xorqi3_noclobber"
[(set (match_operand:QI 0 "std_reg_operand" "=c,c,?c")
(xor:QI (match_operand:QI 1 "src_operand" "%0,rR,rS<>")
(match_operand:QI 2 "lsrc_operand" "rLm,JR,rS<>")))]
"valid_operands (XOR, operands, QImode)"
"@
xor\\t%2,%0
xor3\\t%2,%1,%0
xor3\\t%2,%1,%0"
[(set_attr "type" "binary,binary,binary")
(set_attr "data" "uint16,int16,uint16")])
 
(define_split
[(set (match_operand:QI 0 "std_reg_operand" "")
(xor:QI (match_operand:QI 1 "src_operand" "")
(match_operand:QI 2 "lsrc_operand" "")))
(clobber (reg:CC 21))]
"reload_completed"
[(set (match_dup 0)
(xor:QI (match_dup 1)
(match_dup 2)))]
"")
 
(define_insn "*xorqi3_test"
[(set (reg:CC 21)
(compare:CC (xor:QI (match_operand:QI 1 "src_operand" "%0,rR,rS<>")
(match_operand:QI 2 "lsrc_operand" "rLm,JR,rS<>"))
(const_int 0)))
(clobber (match_scratch:QI 0 "=d,d,d"))]
"valid_operands (XOR, operands, QImode)"
"@
xor\\t%2,%0
xor3\\t%2,%1,%0
xor3\\t%2,%1,%0"
[(set_attr "type" "binarycc,binarycc,binarycc")
(set_attr "data" "uint16,int16,uint16")])
 
(define_insn "*xorqi3_set"
[(set (reg:CC 21)
(compare:CC (xor:QI (match_operand:QI 1 "src_operand" "%0,rR,rS<>")
(match_operand:QI 2 "lsrc_operand" "rLm,JR,rS<>"))
(const_int 0)))
(set (match_operand:QI 0 "ext_reg_operand" "=d,d,d")
(xor:QI (match_dup 1)
(match_dup 2)))]
"valid_operands (XOR, operands, QImode)"
"@
xor\\t%2,%0
xor3\\t%2,%1,%0
xor3\\t%2,%1,%0"
[(set_attr "type" "binarycc,binarycc,binarycc")
(set_attr "data" "uint16,int16,uint16")])
 
;
; LSH/ASH (left)
;
; The C3x and C4x have two shift instructions ASH and LSH
; If the shift count is positive, a left shift is performed
; otherwise a right shift is performed. The number of bits
; shifted is determined by the seven LSBs of the shift count.
; If the absolute value of the count is 32 or greater, the result
; using the LSH instruction is zero; with the ASH insn the result
; is zero or negative 1. Note that the ISO C standard allows
; the result to be machine dependent whenever the shift count
; exceeds the size of the object.
(define_expand "ashlqi3"
[(parallel [(set (match_operand:QI 0 "reg_operand" "")
(ashift:QI (match_operand:QI 1 "src_operand" "")
(match_operand:QI 2 "src_operand" "")))
(clobber (reg:CC 21))])]
""
"legitimize_operands (ASHIFT, operands, QImode);")
 
(define_insn "*ashlqi3_clobber"
[(set (match_operand:QI 0 "reg_operand" "=d,d,?d,c,c,?c")
(ashift:QI (match_operand:QI 1 "src_operand" "0,rR,rS<>,0,rR,rS<>")
(match_operand:QI 2 "src_operand" "rIm,JR,rS<>,rIm,JR,rS<>")))
(clobber (reg:CC 21))]
"valid_operands (ASHIFT, operands, QImode)"
"@
ash\\t%2,%0
ash3\\t%2,%1,%0
ash3\\t%2,%1,%0
ash\\t%2,%0
ash3\\t%2,%1,%0
ash3\\t%2,%1,%0"
[(set_attr "type" "binarycc,binarycc,binarycc,binary,binary,binary")])
; Default to int16 data attr.
 
(define_insn "*ashlqi3_set"
[(set (reg:CC 21)
(compare:CC
(ashift:QI (match_operand:QI 1 "src_operand" "0,rR,rS<>")
(match_operand:QI 2 "src_operand" "rIm,JR,rS<>"))
(const_int 0)))
(set (match_operand:QI 0 "reg_operand" "=d,d,d")
(ashift:QI (match_dup 1)
(match_dup 2)))]
"valid_operands (ASHIFT, operands, QImode)"
"@
ash\\t%2,%0
ash3\\t%2,%1,%0
ash3\\t%2,%1,%0"
[(set_attr "type" "binarycc,binarycc,binarycc")])
; Default to int16 data attr.
 
(define_insn "ashlqi3_noclobber"
[(set (match_operand:QI 0 "std_reg_operand" "=c,c,?c")
(ashift:QI (match_operand:QI 1 "src_operand" "0,rR,rS<>")
(match_operand:QI 2 "src_operand" "rIm,JR,rS<>")))]
"valid_operands (ASHIFT, operands, QImode)"
"@
ash\\t%2,%0
ash3\\t%2,%1,%0
ash3\\t%2,%1,%0"
[(set_attr "type" "binary,binary,binary")])
; Default to int16 data attr.
 
(define_split
[(set (match_operand:QI 0 "std_reg_operand" "")
(ashift:QI (match_operand:QI 1 "src_operand" "")
(match_operand:QI 2 "src_operand" "")))
(clobber (reg:CC 21))]
"reload_completed"
[(set (match_dup 0)
(ashift:QI (match_dup 1)
(match_dup 2)))]
"")
 
; This is only used by lshrhi3_reg where we need a LSH insn that will
; shift both ways.
(define_insn "*lshlqi3_clobber"
[(set (match_operand:QI 0 "reg_operand" "=d,d,?d,c,c,?c")
(ashift:QI (match_operand:QI 1 "src_operand" "0,rR,rS<>,0,rR,rS<>")
(unspec:QI [(match_operand:QI 2 "src_operand" "rIm,JR,rS<>,rIm,JR,rS<>")] UNSPEC_LSH)))
(clobber (reg:CC 21))]
"valid_operands (ASHIFT, operands, QImode)"
"@
lsh\\t%2,%0
lsh3\\t%2,%1,%0
lsh3\\t%2,%1,%0
lsh\\t%2,%0
lsh3\\t%2,%1,%0
lsh3\\t%2,%1,%0"
[(set_attr "type" "binarycc,binarycc,binarycc,binary,binary,binary")])
; Default to int16 data attr.
 
;
; LSH (right)
;
; Logical right shift on the C[34]x works by negating the shift count,
; then emitting a right shift with the shift count negated. This means
; that all actual shift counts in the RTL will be positive.
;
(define_expand "lshrqi3"
[(parallel [(set (match_operand:QI 0 "reg_operand" "")
(lshiftrt:QI (match_operand:QI 1 "src_operand" "")
(match_operand:QI 2 "src_operand" "")))
(clobber (reg:CC 21))])]
""
"legitimize_operands (LSHIFTRT, operands, QImode);")
 
 
(define_insn "*lshrqi3_24_clobber"
[(set (match_operand:QI 0 "reg_operand" "=d,c")
(lshiftrt:QI (match_operand:QI 1 "src_operand" "mr,mr")
(const_int 24)))
(clobber (reg:CC 21))]
"! TARGET_C3X"
"lbu3\\t%1,%0"
[(set_attr "type" "unarycc")])
 
 
(define_insn "*ashrqi3_24_clobber"
[(set (match_operand:QI 0 "reg_operand" "=d,c")
(ashiftrt:QI (match_operand:QI 1 "src_operand" "mr,mr")
(const_int 24)))
(clobber (reg:CC 21))]
"! TARGET_C3X"
"lb3\\t%1,%0"
[(set_attr "type" "unarycc")])
 
 
(define_insn "lshrqi3_16_clobber"
[(set (match_operand:QI 0 "reg_operand" "=d,c")
(lshiftrt:QI (match_operand:QI 1 "src_operand" "mr,mr")
(const_int 16)))
(clobber (reg:CC 21))]
"! TARGET_C3X"
"lhu1\\t%1,%0"
[(set_attr "type" "unarycc")])
 
 
(define_insn "*ashrqi3_16_clobber"
[(set (match_operand:QI 0 "reg_operand" "=d,c")
(ashiftrt:QI (match_operand:QI 1 "src_operand" "mr,mr")
(const_int 16)))
(clobber (reg:CC 21))]
"! TARGET_C3X"
"lh1\\t%1,%0"
[(set_attr "type" "unarycc")])
 
 
; When the shift count is greater than the size of the word
; the result can be implementation specific
(define_insn "*lshrqi3_const_clobber"
[(set (match_operand:QI 0 "reg_operand" "=d,c,?d,?c")
(lshiftrt:QI (match_operand:QI 1 "src_operand" "0,0,r,r")
(match_operand:QI 2 "const_int_operand" "n,n,J,J")))
(clobber (reg:CC 21))]
"valid_operands (LSHIFTRT, operands, QImode)"
"@
lsh\\t%n2,%0
lsh\\t%n2,%0
lsh3\\t%n2,%1,%0
lsh3\\t%n2,%1,%0"
[(set_attr "type" "binarycc,binary,binarycc,binary")])
 
(define_insn "*lshrqi3_const_noclobber"
[(set (match_operand:QI 0 "std_reg_operand" "=c,?c")
(lshiftrt:QI (match_operand:QI 1 "src_operand" "0,r")
(match_operand:QI 2 "const_int_operand" "n,J")))]
"valid_operands (LSHIFTRT, operands, QImode)"
"@
lsh\\t%n2,%0
lsh3\\t%n2,%1,%0"
[(set_attr "type" "binary,binary")])
 
; When the shift count is greater than the size of the word
; the result can be implementation specific
(define_insn "*lshrqi3_const_set"
[(set (reg:CC 21)
(compare:CC
(lshiftrt:QI (match_operand:QI 1 "src_operand" "0,r")
(match_operand:QI 2 "const_int_operand" "n,J"))
(const_int 0)))
(set (match_operand:QI 0 "reg_operand" "=?d,d")
(lshiftrt:QI (match_dup 1)
(match_dup 2)))]
"valid_operands (LSHIFTRT, operands, QImode)"
"@
lsh\\t%n2,%0
lsh3\\t%n2,%1,%0"
[(set_attr "type" "binarycc,binarycc")])
 
(define_insn "*lshrqi3_nonconst_clobber"
[(set (match_operand:QI 0 "reg_operand" "=d,d,?d,c,c,?c")
(lshiftrt:QI (match_operand:QI 1 "src_operand" "0,rR,rS<>,0,rR,rS<>")
(neg:QI (match_operand:QI 2 "src_operand" "rm,R,rS<>,rm,R,rS<>"))))
(clobber (reg:CC 21))]
"valid_operands (LSHIFTRT, operands, QImode)"
"@
lsh\\t%2,%0
lsh3\\t%2,%1,%0
lsh3\\t%2,%1,%0
lsh\\t%2,%0
lsh3\\t%2,%1,%0
lsh3\\t%2,%1,%0"
[(set_attr "type" "binarycc,binarycc,binarycc,binary,binary,binary")])
; Default to int16 data attr.
 
(define_insn "*lshrqi3_nonconst_noclobber"
[(set (match_operand:QI 0 "std_reg_operand" "=c,c,?c")
(lshiftrt:QI (match_operand:QI 1 "src_operand" "0,rR,rS<>")
(neg:QI (match_operand:QI 2 "src_operand" "rm,R,rS<>"))))]
"valid_operands (LSHIFTRT, operands, QImode)"
"@
lsh\\t%2,%0
lsh3\\t%2,%1,%0
lsh3\\t%2,%1,%0"
[(set_attr "type" "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,binary,binarycc,binary")])
 
(define_insn "*ashrqi3_const_noclobber"
[(set (match_operand:QI 0 "std_reg_operand" "=c,?c")
(ashiftrt:QI (match_operand:QI 1 "src_operand" "0,r")
(match_operand:QI 2 "const_int_operand" "n,J")))]
"valid_operands (ASHIFTRT, operands, QImode)"
"@
ash\\t%n2,%0
ash3\\t%n2,%1,%0"
[(set_attr "type" "binarycc,binarycc")])
 
; When the shift count is greater than the size of the word
; the result can be implementation specific
(define_insn "*ashrqi3_const_set"
[(set (reg:CC 21)
(compare:CC
(ashiftrt:QI (match_operand:QI 1 "src_operand" "0,r")
(match_operand:QI 2 "const_int_operand" "n,J"))
(const_int 0)))
(set (match_operand:QI 0 "reg_operand" "=?d,d")
(ashiftrt:QI (match_dup 1)
(match_dup 2)))]
"valid_operands (ASHIFTRT, operands, QImode)"
"@
ash\\t%n2,%0
ash3\\t%n2,%1,%0"
[(set_attr "type" "binarycc,binarycc")])
 
(define_insn "*ashrqi3_nonconst_clobber"
[(set (match_operand:QI 0 "reg_operand" "=d,d,?d,c,c,?c")
(ashiftrt:QI (match_operand:QI 1 "src_operand" "0,rR,rS<>,0,rR,rS<>")
(neg:QI (match_operand:QI 2 "src_operand" "rm,R,rS<>,rm,R,rS<>"))))
(clobber (reg:CC 21))]
"valid_operands (ASHIFTRT, operands, QImode)"
"@
ash\\t%2,%0
ash3\\t%2,%1,%0
ash3\\t%2,%1,%0
ash\\t%2,%0
ash3\\t%2,%1,%0
ash3\\t%2,%1,%0"
[(set_attr "type" "binarycc,binarycc,binarycc,binary,binary,binary")])
; Default to int16 data attr.
 
(define_insn "*ashrqi3_nonconst_noclobber"
[(set (match_operand:QI 0 "std_reg_operand" "=c,c,?c")
(ashiftrt:QI (match_operand:QI 1 "src_operand" "0,rR,rS<>")
(neg:QI (match_operand:QI 2 "src_operand" "rm,R,rS<>"))))]
"valid_operands (ASHIFTRT, operands, QImode)"
"@
ash\\t%2,%0
ash3\\t%2,%1,%0
ash3\\t%2,%1,%0"
[(set_attr "type" "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 optimization pass
; comes before the flow pass (which finds autoincrements) so we're stuck.
; Ideally, GCC requires another loop optimization pass (preferably after
; reload) so that it can hoist invariants out of loops.
; The current solution modifies legitimize_operands () so that small
; constants are forced into a pseudo register.
;
(define_expand "cmpqi"
[(set (reg:CC 21)
(compare:CC (match_operand:QI 0 "src_operand" "")
(match_operand:QI 1 "src_operand" "")))]
""
"legitimize_operands (COMPARE, operands, QImode);
c4x_compare_op0 = operands[0];
c4x_compare_op1 = operands[1];
DONE;")
 
(define_insn "*cmpqi_test"
[(set (reg:CC 21)
(compare:CC (match_operand:QI 0 "src_operand" "r,rR,rS<>")
(match_operand:QI 1 "src_operand" "rIm,JR,rS<>")))]
"valid_operands (COMPARE, operands, QImode)"
"@
cmpi\\t%1,%0
cmpi3\\t%1,%0
cmpi3\\t%1,%0"
[(set_attr "type" "compare,compare,compare")])
 
(define_insn "*cmpqi_test_noov"
[(set (reg:CC_NOOV 21)
(compare:CC_NOOV (match_operand:QI 0 "src_operand" "r,rR,rS<>")
(match_operand:QI 1 "src_operand" "rIm,JR,rS<>")))]
"valid_operands (COMPARE, operands, QImode)"
"@
cmpi\\t%1,%0
cmpi3\\t%1,%0
cmpi3\\t%1,%0"
[(set_attr "type" "compare,compare,compare")])
 
 
;
; BIT-FIELD INSTRUCTIONS
;
 
;
; LBx/LHw (C4x only)
;
(define_expand "extv"
[(parallel [(set (match_operand:QI 0 "reg_operand" "")
(sign_extract:QI (match_operand:QI 1 "src_operand" "")
(match_operand:QI 2 "const_int_operand" "")
(match_operand:QI 3 "const_int_operand" "")))
(clobber (reg:CC 21))])]
"! TARGET_C3X"
"if ((INTVAL (operands[2]) != 8 && INTVAL (operands[2]) != 16)
|| (INTVAL (operands[3]) % INTVAL (operands[2]) != 0))
FAIL;
")
 
(define_insn "*extv_clobber"
[(set (match_operand:QI 0 "reg_operand" "=d,c")
(sign_extract:QI (match_operand:QI 1 "src_operand" "rLm,rLm")
(match_operand:QI 2 "const_int_operand" "n,n")
(match_operand:QI 3 "const_int_operand" "n,n")))
(clobber (reg:CC 21))]
"! TARGET_C3X
&& (INTVAL (operands[2]) == 8 || INTVAL (operands[2]) == 16)
&& (INTVAL (operands[3]) % INTVAL (operands[2]) == 0)"
"*
if (INTVAL (operands[2]) == 8)
{
operands[3] = GEN_INT (INTVAL (operands[3]) / 8);
return \"lb%3\\t%1,%0\";
}
operands[3] = GEN_INT (INTVAL (operands[3]) / 16);
return \"lh%3\\t%1,%0\";
"
[(set_attr "type" "binarycc,binary")
(set_attr "data" "int16,int16")])
 
(define_insn "*extv_clobber_test"
[(set (reg:CC 21)
(compare:CC (sign_extract:QI (match_operand:QI 1 "src_operand" "rLm")
(match_operand:QI 2 "const_int_operand" "n")
(match_operand:QI 3 "const_int_operand" "n"))
(const_int 0)))
(clobber (match_scratch:QI 0 "=d"))]
"! TARGET_C3X
&& (INTVAL (operands[2]) == 8 || INTVAL (operands[2]) == 16)
&& (INTVAL (operands[3]) % INTVAL (operands[2]) == 0)"
"*
if (INTVAL (operands[2]) == 8)
{
operands[3] = GEN_INT (INTVAL (operands[3]) / 8);
return \"lb%3\\t%1,%0\";
}
operands[3] = GEN_INT (INTVAL (operands[3]) / 16);
return \"lh%3\\t%1,%0\";
"
[(set_attr "type" "binarycc")
(set_attr "data" "int16")])
 
(define_insn "*extv_clobber_set"
[(set (reg:CC 21)
(compare:CC (sign_extract:QI (match_operand:QI 1 "src_operand" "rLm")
(match_operand:QI 2 "const_int_operand" "n")
(match_operand:QI 3 "const_int_operand" "n"))
(const_int 0)))
(set (match_operand:QI 0 "reg_operand" "=d")
(sign_extract:QI (match_dup 1)
(match_dup 2)
(match_dup 3)))]
"! TARGET_C3X
&& (INTVAL (operands[2]) == 8 || INTVAL (operands[2]) == 16)
&& (INTVAL (operands[3]) % INTVAL (operands[2]) == 0)"
"*
if (INTVAL (operands[2]) == 8)
{
operands[3] = GEN_INT (INTVAL (operands[3]) / 8);
return \"lb%3\\t%1,%0\";
}
operands[3] = GEN_INT (INTVAL (operands[3]) / 16);
return \"lh%3\\t%1,%0\";
"
[(set_attr "type" "binarycc")
(set_attr "data" "int16")])
 
;
; LBUx/LHUw (C4x only)
;
(define_expand "extzv"
[(parallel [(set (match_operand:QI 0 "reg_operand" "")
(zero_extract:QI (match_operand:QI 1 "src_operand" "")
(match_operand:QI 2 "const_int_operand" "")
(match_operand:QI 3 "const_int_operand" "")))
(clobber (reg:CC 21))])]
"! TARGET_C3X"
"if ((INTVAL (operands[2]) != 8 && INTVAL (operands[2]) != 16)
|| (INTVAL (operands[3]) % INTVAL (operands[2]) != 0))
FAIL;
")
 
(define_insn "*extzv_clobber"
[(set (match_operand:QI 0 "reg_operand" "=d,c")
(zero_extract:QI (match_operand:QI 1 "src_operand" "rLm,rLm")
(match_operand:QI 2 "const_int_operand" "n,n")
(match_operand:QI 3 "const_int_operand" "n,n")))
(clobber (reg:CC 21))]
"! TARGET_C3X
&& (INTVAL (operands[2]) == 8 || INTVAL (operands[2]) == 16)
&& (INTVAL (operands[3]) % INTVAL (operands[2]) == 0)"
"*
if (INTVAL (operands[2]) == 8)
{
operands[3] = GEN_INT (INTVAL (operands[3]) / 8);
return \"lbu%3\\t%1,%0\";
}
operands[3] = GEN_INT (INTVAL (operands[3]) / 16);
return \"lhu%3\\t%1,%0\";
"
[(set_attr "type" "binarycc,binary")
(set_attr "data" "uint16,uint16")])
 
(define_insn "*extzv_test"
[(set (reg:CC 21)
(compare:CC (zero_extract:QI (match_operand:QI 1 "src_operand" "rLm")
(match_operand:QI 2 "const_int_operand" "n")
(match_operand:QI 3 "const_int_operand" "n"))
(const_int 0)))
(clobber (match_scratch:QI 0 "=d"))]
"! TARGET_C3X
&& (INTVAL (operands[2]) == 8 || INTVAL (operands[2]) == 16)
&& (INTVAL (operands[3]) % INTVAL (operands[2]) == 0)"
"*
if (INTVAL (operands[2]) == 8)
{
operands[3] = GEN_INT (INTVAL (operands[3]) / 8);
return \"lbu%3\\t%1,%0\";
}
operands[3] = GEN_INT (INTVAL (operands[3]) / 16);
return \"lhu%3\\t%1,%0\";
"
[(set_attr "type" "binarycc")
(set_attr "data" "uint16")])
 
(define_insn "*extzv_set"
[(set (reg:CC 21)
(compare:CC (zero_extract:QI (match_operand:QI 1 "src_operand" "rLm")
(match_operand:QI 2 "const_int_operand" "n")
(match_operand:QI 3 "const_int_operand" "n"))
(const_int 0)))
(set (match_operand:QI 0 "ext_reg_operand" "=d")
(zero_extract:QI (match_dup 1)
(match_dup 2)
(match_dup 3)))]
"! TARGET_C3X
&& (INTVAL (operands[2]) == 8 || INTVAL (operands[2]) == 16)
&& (INTVAL (operands[3]) % INTVAL (operands[2]) == 0)"
"*
if (INTVAL (operands[2]) == 8)
{
/* 8 bit extract. */
operands[3] = GEN_INT (INTVAL (operands[3]) / 8);
return \"lbu%3\\t%1,%0\";
}
/* 16 bit extract. */
operands[3] = GEN_INT (INTVAL (operands[3]) / 16);
return \"lhu%3\\t%1,%0\";
"
[(set_attr "type" "binarycc")
(set_attr "data" "uint16")])
 
;
; MBx/MHw (C4x only)
;
(define_expand "insv"
[(parallel [(set (zero_extract:QI (match_operand:QI 0 "reg_operand" "")
(match_operand:QI 1 "const_int_operand" "")
(match_operand:QI 2 "const_int_operand" ""))
(match_operand:QI 3 "src_operand" ""))
(clobber (reg:CC 21))])]
"! TARGET_C3X"
"if (! (((INTVAL (operands[1]) == 8 || INTVAL (operands[1]) == 16)
&& (INTVAL (operands[2]) % INTVAL (operands[1]) == 0))
|| (INTVAL (operands[1]) == 24 && INTVAL (operands[2]) == 8)))
FAIL;
")
 
(define_insn "*insv_clobber"
[(set (zero_extract:QI (match_operand:QI 0 "reg_operand" "+d,c")
(match_operand:QI 1 "const_int_operand" "n,n")
(match_operand:QI 2 "const_int_operand" "n,n"))
(match_operand:QI 3 "src_operand" "rLm,rLm"))
(clobber (reg:CC 21))]
"! TARGET_C3X
&& (((INTVAL (operands[1]) == 8 || INTVAL (operands[1]) == 16)
&& (INTVAL (operands[2]) % INTVAL (operands[1]) == 0))
|| (INTVAL (operands[1]) == 24 && INTVAL (operands[2]) == 8))"
"*
if (INTVAL (operands[1]) == 8)
{
/* 8 bit insert. */
operands[2] = GEN_INT (INTVAL (operands[2]) / 8);
return \"mb%2\\t%3,%0\";
}
else if (INTVAL (operands[1]) == 16)
{
/* 16 bit insert. */
operands[2] = GEN_INT (INTVAL (operands[2]) / 16);
return \"mh%2\\t%3,%0\";
}
/* 24 bit insert. */
return \"lwl1\\t%3,%0\";
"
[(set_attr "type" "binarycc,binary")
(set_attr "data" "uint16,uint16")])
 
(define_peephole
[(parallel [(set (zero_extract:QI (match_operand:QI 0 "ext_reg_operand" "+d")
(match_operand:QI 1 "const_int_operand" "n")
(match_operand:QI 2 "const_int_operand" "n"))
(match_operand:QI 3 "src_operand" "rLm"))
(clobber (reg:CC 21))])
(set (reg:CC 21)
(compare:CC (match_dup 0) (const_int 0)))]
"! TARGET_C3X
&& (INTVAL (operands[1]) == 8 || INTVAL (operands[1]) == 16)
&& (INTVAL (operands[2]) % INTVAL (operands[1]) == 0)"
"*
if (INTVAL (operands[1]) == 8)
{
operands[2] = GEN_INT (INTVAL (operands[2]) / 8);
return \"mb%2\\t%3,%0\";
}
operands[2] = GEN_INT (INTVAL (operands[2]) / 16);
return \"mh%2\\t%3,%0\";
"
[(set_attr "type" "binarycc")
(set_attr "data" "uint16")])
 
 
; TWO OPERAND FLOAT INSTRUCTIONS
;
 
;
; LDF/STF
;
; If one of the operands is not a register, then we should
; emit two insns, using a scratch register. This will produce
; better code in loops if the source operand is invariant, since
; the source reload can be optimized out. During reload we cannot
; use change_address or force_reg.
(define_expand "movqf"
[(set (match_operand:QF 0 "src_operand" "")
(match_operand:QF 1 "src_operand" ""))]
""
"
{
if (c4x_emit_move_sequence (operands, QFmode))
DONE;
}")
 
; This can generate invalid stack slot displacements
(define_split
[(set (match_operand:QI 0 "reg_operand" "")
(unspec:QI [(match_operand:QF 1 "reg_operand" "")] UNSPEC_STOREQF_INT))]
"reload_completed"
[(set (match_dup 3) (match_dup 1))
(set (match_dup 0) (match_dup 2))]
"operands[2] = assign_stack_temp (QImode, GET_MODE_SIZE (QImode), 0);
operands[3] = copy_rtx (operands[2]);
PUT_MODE (operands[3], QFmode);")
 
 
(define_insn "storeqf_int"
[(set (match_operand:QI 0 "reg_operand" "=r")
(unspec:QI [(match_operand:QF 1 "reg_operand" "f")] UNSPEC_STOREQF_INT))]
""
"#"
[(set_attr "type" "multi")])
 
(define_split
[(parallel [(set (match_operand:QI 0 "reg_operand" "")
(unspec:QI [(match_operand:QF 1 "reg_operand" "")] UNSPEC_STOREQF_INT))
(clobber (reg:CC 21))])]
"reload_completed"
[(set (mem:QF (pre_inc:QI (reg:QI 20)))
(match_dup 1))
(parallel [(set (match_dup 0)
(mem:QI (post_dec:QI (reg:QI 20))))
(clobber (reg:CC 21))])]
"")
 
 
; We need accurate death notes for this...
;(define_peephole
; [(set (match_operand:QF 0 "reg_operand" "=f")
; (match_operand:QF 1 "memory_operand" "m"))
; (set (mem:QF (pre_inc:QI (reg:QI 20)))
; (match_dup 0))
; (parallel [(set (match_operand:QI 2 "reg_operand" "r")
; (mem:QI (post_dec:QI (reg:QI 20))))
; (clobber (reg:CC 21))])]
; ""
; "ldiu\\t%1,%0")
 
(define_insn "storeqf_int_clobber"
[(parallel [(set (match_operand:QI 0 "reg_operand" "=r")
(unspec:QI [(match_operand:QF 1 "reg_operand" "f")] UNSPEC_STOREQF_INT))
(clobber (reg:CC 21))])]
""
"#"
[(set_attr "type" "multi")])
 
 
; This can generate invalid stack slot displacements
(define_split
[(set (match_operand:QF 0 "reg_operand" "")
(unspec:QF [(match_operand:QI 1 "reg_operand" "")] UNSPEC_LOADQF_INT))]
"reload_completed"
[(set (match_dup 2) (match_dup 1))
(set (match_dup 0) (match_dup 3))]
"operands[2] = assign_stack_temp (QImode, GET_MODE_SIZE (QImode), 0);
operands[3] = copy_rtx (operands[2]);
PUT_MODE (operands[3], QFmode);")
 
 
(define_insn "loadqf_int"
[(set (match_operand:QF 0 "reg_operand" "=f")
(unspec:QF [(match_operand:QI 1 "reg_operand" "r")] UNSPEC_LOADQF_INT))]
""
"#"
[(set_attr "type" "multi")])
 
(define_split
[(parallel [(set (match_operand:QF 0 "reg_operand" "")
(unspec:QF [(match_operand:QI 1 "reg_operand" "")] UNSPEC_LOADQF_INT))
(clobber (reg:CC 21))])]
"reload_completed"
[(set (mem:QI (pre_inc:QI (reg:QI 20)))
(match_dup 1))
(parallel [(set (match_dup 0)
(mem:QF (post_dec:QI (reg:QI 20))))
(clobber (reg:CC 21))])]
"")
 
(define_insn "loadqf_int_clobber"
[(parallel [(set (match_operand:QF 0 "reg_operand" "=f")
(unspec:QF [(match_operand:QI 1 "reg_operand" "r")] UNSPEC_LOADQF_INT))
(clobber (reg:CC 21))])]
""
"#"
[(set_attr "type" "multi")])
 
; We must provide an alternative to store to memory in case we have to
; spill a register.
(define_insn "movqf_noclobber"
[(set (match_operand:QF 0 "dst_operand" "=f,m")
(match_operand:QF 1 "src_operand" "fHm,f"))]
"REG_P (operands[0]) || REG_P (operands[1])"
"@
ldfu\\t%1,%0
stf\\t%1,%0"
[(set_attr "type" "unary,store")])
 
;(define_insn "*movqf_clobber"
; [(set (match_operand:QF 0 "reg_operand" "=f")
; (match_operand:QF 1 "src_operand" "fHm"))
; (clobber (reg:CC 21))]
; "0"
; "ldf\\t%1,%0"
; [(set_attr "type" "unarycc")])
 
(define_insn "*movqf_test"
[(set (reg:CC 21)
(compare:CC (match_operand:QF 1 "src_operand" "fHm")
(const_int 0)))
(clobber (match_scratch:QF 0 "=f"))]
""
"ldf\\t%1,%0"
[(set_attr "type" "unarycc")])
 
(define_insn "*movqf_set"
[(set (reg:CC 21)
(compare:CC (match_operand:QF 1 "src_operand" "fHm")
(match_operand:QF 2 "fp_zero_operand" "G")))
(set (match_operand:QF 0 "reg_operand" "=f")
(match_dup 1))]
""
"ldf\\t%1,%0"
[(set_attr "type" "unarycc")])
 
 
(define_insn "*movqf_parallel"
[(set (match_operand:QF 0 "parallel_operand" "=q,S<>!V,q,S<>!V")
(match_operand:QF 1 "parallel_operand" "S<>!V,q,S<>!V,q"))
(set (match_operand:QF 2 "parallel_operand" "=q,S<>!V,S<>!V,q")
(match_operand:QF 3 "parallel_operand" "S<>!V,q,q,S<>!V"))]
"TARGET_PARALLEL && valid_parallel_load_store (operands, QFmode)"
"@
ldf1\\t%1,%0\\n||\\tldf2\\t%3,%2
stf1\\t%1,%0\\n||\\tstf2\\t%3,%2
ldf\\t%1,%0\\n||\\tstf\\t%3,%2
ldf\\t%3,%2\\n||\\tstf\\t%1,%0"
[(set_attr "type" "load_load,store_store,load_store,store_load")])
 
 
;
; PUSH/POP
;
(define_insn "pushqf"
[(set (mem:QF (pre_inc:QI (reg:QI 20)))
(match_operand:QF 0 "reg_operand" "f"))]
""
"pushf\\t%0"
[(set_attr "type" "push")])
 
(define_insn "popqf"
[(set (match_operand:QF 0 "reg_operand" "=f")
(mem:QF (post_dec:QI (reg:QI 20))))
(clobber (reg:CC 21))]
""
"popf\\t%0"
[(set_attr "type" "pop")])
 
(define_insn "popqf_unspec"
[(set (unspec:QF [(match_operand:QF 0 "reg_operand" "=f")] UNSPEC_POPQF)
(mem:QF (post_dec:QI (reg:QI 20))))
(clobber (match_dup 0))
(clobber (reg:CC 21))]
""
"popf\\t%0"
[(set_attr "type" "pop")])
 
;
; ABSF
;
(define_expand "absqf2"
[(parallel [(set (match_operand:QF 0 "reg_operand" "")
(abs:QF (match_operand:QF 1 "src_operand" "")))
(clobber (reg:CC_NOOV 21))])]
""
"")
 
(define_insn "*absqf2_clobber"
[(set (match_operand:QF 0 "reg_operand" "=f")
(abs:QF (match_operand:QF 1 "src_operand" "fHm")))
(clobber (reg:CC_NOOV 21))]
""
"absf\\t%1,%0"
[(set_attr "type" "unarycc")])
 
(define_insn "*absqf2_test"
[(set (reg:CC_NOOV 21)
(compare:CC_NOOV (abs:QF (match_operand:QF 1 "src_operand" "fHm"))
(match_operand:QF 2 "fp_zero_operand" "G")))
(clobber (match_scratch:QF 0 "=f"))]
""
"absf\\t%1,%0"
[(set_attr "type" "unarycc")])
 
(define_insn "*absqf2_set"
[(set (reg:CC_NOOV 21)
(compare:CC_NOOV (abs:QF (match_operand:QF 1 "src_operand" "fHm"))
(match_operand:QF 2 "fp_zero_operand" "G")))
(set (match_operand:QF 0 "reg_operand" "=f")
(abs:QF (match_dup 1)))]
 
""
"absf\\t%1,%0"
[(set_attr "type" "unarycc")])
 
;
; NEGF
;
(define_expand "negqf2"
[(parallel [(set (match_operand:QF 0 "reg_operand" "")
(neg:QF (match_operand:QF 1 "src_operand" "")))
(clobber (reg:CC_NOOV 21))])]
""
"")
 
(define_insn "*negqf2_clobber"
[(set (match_operand:QF 0 "reg_operand" "=f")
(neg:QF (match_operand:QF 1 "src_operand" "fHm")))
(clobber (reg:CC_NOOV 21))]
""
"negf\\t%1,%0"
[(set_attr "type" "unarycc")])
 
(define_insn "*negqf2_test"
[(set (reg:CC_NOOV 21)
(compare:CC_NOOV (neg:QF (match_operand:QF 1 "src_operand" "fHm"))
(match_operand:QF 2 "fp_zero_operand" "G")))
(clobber (match_scratch:QF 0 "=f"))]
""
"negf\\t%1,%0"
[(set_attr "type" "unarycc")])
 
(define_insn "*negqf2_set"
[(set (reg:CC_NOOV 21)
(compare:CC_NOOV (neg:QF (match_operand:QF 1 "src_operand" "fHm"))
(match_operand:QF 2 "fp_zero_operand" "G")))
(set (match_operand:QF 0 "reg_operand" "=f")
(neg:QF (match_dup 1)))]
""
"negf\\t%1,%0"
[(set_attr "type" "unarycc")])
 
;
; FLOAT
;
(define_insn "floatqiqf2"
[(set (match_operand:QF 0 "reg_operand" "=f")
(float:QF (match_operand:QI 1 "src_operand" "rIm")))
(clobber (reg:CC 21))]
""
"float\\t%1,%0"
[(set_attr "type" "unarycc")])
 
(define_insn "*floatqiqf2_set"
[(set (reg:CC 21)
(compare:CC (float:QF (match_operand:QI 1 "src_operand" "rIm"))
(match_operand:QF 2 "fp_zero_operand" "G")))
(set (match_operand:QF 0 "reg_operand" "=f")
(float:QF (match_dup 1)))]
""
"float\\t%1,%0"
[(set_attr "type" "unarycc")])
 
; Unsigned conversions are a little tricky because we need to
; add the value for the high bit if necessary.
;
;
(define_expand "floatunsqiqf2"
[(set (match_dup 2) (match_dup 3))
(parallel [(set (reg:CC 21)
(compare:CC (float:QF (match_operand:QI 1 "src_operand" ""))
(match_dup 3)))
(set (match_dup 4)
(float:QF (match_dup 1)))])
(set (match_dup 2)
(if_then_else:QF (lt (reg:CC 21) (const_int 0))
(match_dup 5)
(match_dup 2)))
(parallel [(set (match_operand:QF 0 "reg_operand" "")
(plus:QF (match_dup 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);
operands[5] = gen_reg_rtx (QFmode);
emit_move_insn (operands[5], CONST_DOUBLE_ATOF (\"4294967296.0\", QFmode));")
 
(define_expand "floatunsqihf2"
[(set (match_dup 2) (match_dup 3))
(parallel [(set (reg:CC 21)
(compare:CC (float:HF (match_operand:QI 1 "src_operand" ""))
(match_dup 3)))
(set (match_dup 4)
(float:HF (match_dup 1)))])
(set (match_dup 2)
(if_then_else:HF (lt (reg:CC 21) (const_int 0))
(match_dup 5)
(match_dup 2)))
(parallel [(set (match_operand:HF 0 "reg_operand" "")
(plus:HF (match_dup 2) (match_dup 4)))
(clobber (reg:CC_NOOV 21))])]
""
"operands[2] = gen_reg_rtx (HFmode);
operands[3] = CONST0_RTX (HFmode);
operands[4] = gen_reg_rtx (HFmode);
operands[5] = gen_reg_rtx (HFmode);
emit_move_insn (operands[5], CONST_DOUBLE_ATOF (\"4294967296.0\", HFmode));")
 
(define_insn "floatqihf2"
[(set (match_operand:HF 0 "reg_operand" "=h")
(float:HF (match_operand:QI 1 "src_operand" "rIm")))
(clobber (reg:CC 21))]
""
"float\\t%1,%0"
[(set_attr "type" "unarycc")])
 
(define_insn "*floatqihf2_set"
[(set (reg:CC 21)
(compare:CC (float:HF (match_operand:QI 1 "src_operand" "rIm"))
(match_operand:QF 2 "fp_zero_operand" "G")))
(set (match_operand:HF 0 "reg_operand" "=h")
(float:HF (match_dup 1)))]
""
"float\\t%1,%0"
[(set_attr "type" "unarycc")])
 
;
; FIX
;
(define_insn "fixqfqi_clobber"
[(set (match_operand:QI 0 "reg_operand" "=d,c")
(fix:QI (match_operand:QF 1 "src_operand" "fHm,fHm")))
(clobber (reg:CC 21))]
""
"fix\\t%1,%0"
[(set_attr "type" "unarycc")])
 
(define_insn "*fixqfqi_set"
[(set (reg:CC 21)
(compare:CC (fix:QI (match_operand:QF 1 "src_operand" "fHm"))
(const_int 0)))
(set (match_operand:QI 0 "ext_reg_operand" "=d")
(fix:QI (match_dup 1)))]
""
"fix\\t%1,%0"
[(set_attr "type" "unarycc")])
 
;
; The C[34]x fix instruction implements a floor, not a straight trunc,
; so we have to invert the number, fix it, and reinvert it if negative
;
(define_expand "fix_truncqfqi2"
[(parallel [(set (match_dup 2)
(fix:QI (match_operand:QF 1 "src_operand" "")))
(clobber (reg:CC 21))])
(parallel [(set (match_dup 3) (neg:QF (match_dup 1)))
(clobber (reg:CC_NOOV 21))])
(parallel [(set (match_dup 4) (fix:QI (match_dup 3)))
(clobber (reg:CC 21))])
(parallel [(set (reg:CC_NOOV 21)
(compare:CC_NOOV (neg:QI (match_dup 4)) (const_int 0)))
(set (match_dup 5) (neg:QI (match_dup 4)))])
(set (match_dup 2)
(if_then_else:QI (le (reg:CC 21) (const_int 0))
(match_dup 5)
(match_dup 2)))
(set (match_operand:QI 0 "reg_operand" "=r") (match_dup 2))]
""
"if (TARGET_FAST_FIX)
{
emit_insn (gen_fixqfqi_clobber (operands[0], operands[1]));
DONE;
}
operands[2] = gen_reg_rtx (QImode);
operands[3] = gen_reg_rtx (QFmode);
operands[4] = gen_reg_rtx (QImode);
operands[5] = gen_reg_rtx (QImode);
")
 
(define_expand "fix_truncqfhi2"
[(parallel [(set (match_operand:HI 0 "reg_operand" "")
(fix:HI (match_operand:QF 1 "src_operand" "")))
(clobber (reg:CC 21))])]
""
"c4x_emit_libcall (fix_truncqfhi2_libfunc, FIX, HImode, QFmode, 2, operands);
DONE;")
 
(define_expand "fixuns_truncqfqi2"
[(parallel [(set (match_dup 2)
(fix:QI (match_operand:QF 1 "src_operand" "fHm")))
(clobber (reg:CC 21))])
(parallel [(set (match_dup 3)
(minus:QF (match_dup 1) (match_dup 5)))
(clobber (reg:CC_NOOV 21))])
(parallel [(set (reg:CC 21)
(compare:CC (fix:QI (match_dup 3))
(const_int 0)))
(set (match_dup 4)
(fix:QI (match_dup 3)))])
(parallel [(set (match_dup 4) (unspec:QI [(match_dup 2)] UNSPEC_LDIV))
(use (reg:CC 21))])
(set (match_operand:QI 0 "reg_operand" "=r") (match_dup 4))]
""
"operands[2] = gen_reg_rtx (QImode);
operands[3] = gen_reg_rtx (QFmode);
operands[4] = gen_reg_rtx (QImode);
operands[5] = gen_reg_rtx (QFmode);
emit_move_insn (operands[5], CONST_DOUBLE_ATOF (\"4294967296.0\", 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_libfunc, UNSIGNED_FIX,
HImode, QFmode, 2, operands);
DONE;")
 
;
; RCPF
;
(define_insn "rcpfqf_clobber"
[(set (match_operand:QF 0 "reg_operand" "=f")
(unspec:QF [(match_operand:QF 1 "src_operand" "fHm")] UNSPEC_RCPF))
(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:QF [(match_operand:QF 1 "src_operand" "fHm")] UNSPEC_RSQRF))
(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:QF [(match_operand:QF 1 "src_operand" "fHm")] UNSPEC_RND))
(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:QF [(match_operand:QF 1 "src_operand" "")] UNSPEC_RSQRF))
(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:QF [(match_dup 4)] UNSPEC_RND))
(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] = CONST_DOUBLE_ATOF (\"0.5\", QFmode);
operands[6] = CONST_DOUBLE_ATOF (\"1.5\", QFmode);")
 
(define_expand "sqrtqf2"
[(parallel [(set (match_operand:QF 0 "reg_operand" "")
(sqrt:QF (match_operand:QF 1 "src_operand" "")))
(clobber (reg:CC 21))])]
"! TARGET_C3X && TARGET_INLINE"
"emit_insn (gen_sqrtqf2_inline (operands[0], operands[1]));
DONE;")
 
;
; TOIEEE / FRIEEE
;
(define_insn "toieee"
[(set (match_operand:QF 0 "reg_operand" "=f")
(unspec:QF [(match_operand:QF 1 "src_operand" "fHm")] UNSPEC_TOIEEE))
(clobber (reg:CC 21))]
""
"toieee\\t%1,%0")
 
(define_insn "frieee"
[(set (match_operand:QF 0 "reg_operand" "=f")
(unspec:QF [(match_operand:QF 1 "memory_operand" "m")] UNSPEC_FRIEEE))
(clobber (reg:CC 21))]
""
"frieee\\t%1,%0")
 
;
; THREE OPERAND FLOAT INSTRUCTIONS
;
 
;
; ADDF
;
(define_expand "addqf3"
[(parallel [(set (match_operand:QF 0 "reg_operand" "")
(plus:QF (match_operand:QF 1 "src_operand" "")
(match_operand:QF 2 "src_operand" "")))
(clobber (reg:CC_NOOV 21))])]
""
"legitimize_operands (PLUS, operands, QFmode);")
 
(define_insn "*addqf3_clobber"
[(set (match_operand:QF 0 "reg_operand" "=f,f,?f")
(plus:QF (match_operand:QF 1 "src_operand" "%0,fR,fS<>")
(match_operand:QF 2 "src_operand" "fHm,R,fS<>")))
(clobber (reg:CC_NOOV 21))]
"valid_operands (PLUS, operands, QFmode)"
"@
addf\\t%2,%0
addf3\\t%2,%1,%0
addf3\\t%2,%1,%0"
[(set_attr "type" "binarycc,binarycc,binarycc")])
 
(define_insn "*addqf3_test"
[(set (reg:CC_NOOV 21)
(compare:CC_NOOV (plus:QF (match_operand:QF 1 "src_operand" "%0,fR,fS<>")
(match_operand:QF 2 "src_operand" "fHm,R,fS<>"))
(match_operand:QF 3 "fp_zero_operand" "G,G,G")))
(clobber (match_scratch:QF 0 "=f,f,?f"))]
"valid_operands (PLUS, operands, QFmode)"
"@
addf\\t%2,%0
addf3\\t%2,%1,%0
addf3\\t%2,%1,%0"
[(set_attr "type" "binarycc,binarycc,binarycc")])
 
(define_insn "*addqf3_set"
[(set (reg:CC_NOOV 21)
(compare:CC_NOOV (plus:QF (match_operand:QF 1 "src_operand" "%0,fR,fS<>")
(match_operand:QF 2 "src_operand" "fHm,R,fS<>"))
(match_operand:QF 3 "fp_zero_operand" "G,G,G")))
(set (match_operand:QF 0 "reg_operand" "=f,f,?f")
(plus:QF (match_dup 1)
(match_dup 2)))]
"valid_operands (PLUS, operands, QFmode)"
"@
addf\\t%2,%0
addf3\\t%2,%1,%0
addf3\\t%2,%1,%0"
[(set_attr "type" "binarycc,binarycc,binarycc")])
 
;
; SUBF/SUBRF
;
(define_expand "subqf3"
[(parallel [(set (match_operand:QF 0 "reg_operand" "")
(minus:QF (match_operand:QF 1 "src_operand" "")
(match_operand:QF 2 "src_operand" "")))
(clobber (reg:CC_NOOV 21))])]
""
"legitimize_operands (MINUS, operands, QFmode);")
 
(define_insn "*subqf3_clobber"
[(set (match_operand:QF 0 "reg_operand" "=f,f,f,?f")
(minus:QF (match_operand:QF 1 "src_operand" "0,fHm,fR,fS<>")
(match_operand:QF 2 "src_operand" "fHm,0,R,fS<>")))
(clobber (reg:CC_NOOV 21))]
"valid_operands (MINUS, operands, QFmode)"
"@
subf\\t%2,%0
subrf\\t%1,%0
subf3\\t%2,%1,%0
subf3\\t%2,%1,%0"
[(set_attr "type" "binarycc,binarycc,binarycc,binarycc")])
 
(define_insn "*subqf3_test"
[(set (reg:CC_NOOV 21)
(compare:CC_NOOV (minus:QF (match_operand:QF 1 "src_operand" "0,fHm,fR,fS<>")
(match_operand:QF 2 "src_operand" "fHm,0,R,fS<>"))
(match_operand:QF 3 "fp_zero_operand" "G,G,G,G")))
(clobber (match_scratch:QF 0 "=f,f,f,?f"))]
"valid_operands (MINUS, operands, QFmode)"
"@
subf\\t%2,%0
subrf\\t%1,%0
subf3\\t%2,%1,%0
subf3\\t%2,%1,%0"
[(set_attr "type" "binarycc,binarycc,binarycc,binarycc")])
 
(define_insn "*subqf3_set"
[(set (reg:CC_NOOV 21)
(compare:CC_NOOV (minus:QF (match_operand:QF 1 "src_operand" "0,fHm,fR,fS<>")
(match_operand:QF 2 "src_operand" "fHm,0,R,fS<>"))
(match_operand:QF 3 "fp_zero_operand" "G,G,G,G")))
(set (match_operand:QF 0 "reg_operand" "=f,f,f,?f")
(minus:QF (match_dup 1)
(match_dup 2)))]
"valid_operands (MINUS, operands, QFmode)"
"@
subf\\t%2,%0
subrf\\t%1,%0
subf3\\t%2,%1,%0
subf3\\t%2,%1,%0"
[(set_attr "type" "binarycc,binarycc,binarycc,binarycc")])
 
;
; MPYF
;
(define_expand "mulqf3"
[(parallel [(set (match_operand:QF 0 "reg_operand" "")
(mult:QF (match_operand:QF 1 "src_operand" "")
(match_operand:QF 2 "src_operand" "")))
(clobber (reg:CC_NOOV 21))])]
""
"legitimize_operands (MULT, operands, QFmode);")
 
(define_insn "*mulqf3_clobber"
[(set (match_operand:QF 0 "reg_operand" "=f,f,?f")
(mult:QF (match_operand:QF 1 "src_operand" "%0,fR,fS<>")
(match_operand:QF 2 "src_operand" "fHm,R,fS<>")))
(clobber (reg:CC_NOOV 21))]
"valid_operands (MULT, operands, QFmode)"
"@
mpyf\\t%2,%0
mpyf3\\t%2,%1,%0
mpyf3\\t%2,%1,%0"
[(set_attr "type" "binarycc,binarycc,binarycc")])
 
(define_insn "*mulqf3_test"
[(set (reg:CC_NOOV 21)
(compare:CC_NOOV (mult:QF (match_operand:QF 1 "src_operand" "%0,fR,fS<>")
(match_operand:QF 2 "src_operand" "fHm,R,fS<>"))
(match_operand:QF 3 "fp_zero_operand" "G,G,G")))
(clobber (match_scratch:QF 0 "=f,f,?f"))]
"valid_operands (MULT, operands, QFmode)"
"@
mpyf\\t%2,%0
mpyf3\\t%2,%1,%0
mpyf3\\t%2,%1,%0"
[(set_attr "type" "binarycc,binarycc,binarycc")])
 
(define_insn "*mulqf3_set"
[(set (reg:CC_NOOV 21)
(compare:CC_NOOV (mult:QF (match_operand:QF 1 "src_operand" "%0,fR,fS<>")
(match_operand:QF 2 "src_operand" "fHm,R,fS<>"))
(match_operand:QF 3 "fp_zero_operand" "G,G,G")))
(set (match_operand:QF 0 "reg_operand" "=f,f,?f")
(mult:QF (match_dup 1)
(match_dup 2)))]
"valid_operands (MULT, operands, QFmode)"
"@
mpyf\\t%2,%0
mpyf3\\t%2,%1,%0
mpyf3\\t%2,%1,%0"
[(set_attr "type" "binarycc,binarycc,binarycc")])
 
;
; CMPF
;
(define_expand "cmpqf"
[(set (reg:CC 21)
(compare:CC (match_operand:QF 0 "src_operand" "")
(match_operand:QF 1 "src_operand" "")))]
""
"legitimize_operands (COMPARE, operands, QFmode);
c4x_compare_op0 = operands[0];
c4x_compare_op1 = operands[1];
DONE;")
 
(define_insn "*cmpqf"
[(set (reg:CC 21)
(compare:CC (match_operand:QF 0 "src_operand" "f,fR,fS<>")
(match_operand:QF 1 "src_operand" "fHm,R,fS<>")))]
"valid_operands (COMPARE, operands, QFmode)"
"@
cmpf\\t%1,%0
cmpf3\\t%1,%0
cmpf3\\t%1,%0"
[(set_attr "type" "compare,compare,compare")])
 
(define_insn "*cmpqf_noov"
[(set (reg:CC_NOOV 21)
(compare:CC_NOOV (match_operand:QF 0 "src_operand" "f,fR,fS<>")
(match_operand:QF 1 "src_operand" "fHm,R,fS<>")))]
"valid_operands (COMPARE, operands, QFmode)"
"@
cmpf\\t%1,%0
cmpf3\\t%1,%0
cmpf3\\t%1,%0"
[(set_attr "type" "compare,compare,compare")])
 
; Inlined float divide for C4x
(define_expand "divqf3_inline"
[(parallel [(set (match_dup 3)
(unspec:QF [(match_operand:QF 2 "src_operand" "")] UNSPEC_RCPF))
(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:QF [(match_dup 3)] UNSPEC_RND))
(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))])]
"! TARGET_C3X && TARGET_INLINE"
"emit_insn (gen_divqf3_inline (operands[0], operands[1], operands[2]));
DONE;")
 
;
; CONDITIONAL MOVES
;
 
; ??? We should make these pattern fail if the src operand combination
; is not valid. Although reload will fix things up, it will introduce
; extra load instructions that won't be hoisted out of a loop.
 
(define_insn "*ldi_conditional"
[(set (match_operand:QI 0 "reg_operand" "=r,r")
(if_then_else:QI (match_operator 1 "comparison_operator"
[(reg:CC 21) (const_int 0)])
(match_operand:QI 2 "src_operand" "rIm,0")
(match_operand:QI 3 "src_operand" "0,rIm")))]
"valid_operands (IF_THEN_ELSE, operands, QImode)"
"@
ldi%1\\t%2,%0
ldi%I1\\t%3,%0"
[(set_attr "type" "binary")])
 
(define_insn "*ldi_conditional_noov"
[(set (match_operand:QI 0 "reg_operand" "=r,r")
(if_then_else:QI (match_operator 1 "comparison_operator"
[(reg:CC_NOOV 21) (const_int 0)])
(match_operand:QI 2 "src_operand" "rIm,0")
(match_operand:QI 3 "src_operand" "0,rIm")))]
"GET_CODE (operands[1]) != LE
&& GET_CODE (operands[1]) != GE
&& GET_CODE (operands[1]) != LT
&& GET_CODE (operands[1]) != GT
&& valid_operands (IF_THEN_ELSE, operands, QImode)"
"@
ldi%1\\t%2,%0
ldi%I1\\t%3,%0"
[(set_attr "type" "binary")])
 
(define_insn "*ldi_on_overflow"
[(set (match_operand:QI 0 "reg_operand" "=r")
(unspec:QI [(match_operand:QI 1 "src_operand" "rIm")] UNSPEC_LDIV))
(use (reg:CC 21))]
""
"ldiv\\t%1,%0"
[(set_attr "type" "unary")])
 
; 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_fmt_ee (code, VOIDmode, ccreg, const0_rtx),
operands[2], operands[3])));
DONE;}")
(define_insn "*ldf_conditional"
[(set (match_operand:QF 0 "reg_operand" "=f,f")
(if_then_else:QF (match_operator 1 "comparison_operator"
[(reg:CC 21) (const_int 0)])
(match_operand:QF 2 "src_operand" "fHm,0")
(match_operand:QF 3 "src_operand" "0,fHm")))]
"valid_operands (IF_THEN_ELSE, operands, QFmode)"
"@
ldf%1\\t%2,%0
ldf%I1\\t%3,%0"
[(set_attr "type" "binary")])
 
(define_insn "*ldf_conditional_noov"
[(set (match_operand:QF 0 "reg_operand" "=f,f")
(if_then_else:QF (match_operator 1 "comparison_operator"
[(reg:CC_NOOV 21) (const_int 0)])
(match_operand:QF 2 "src_operand" "fHm,0")
(match_operand:QF 3 "src_operand" "0,fHm")))]
"GET_CODE (operands[1]) != LE
&& GET_CODE (operands[1]) != GE
&& GET_CODE (operands[1]) != LT
&& GET_CODE (operands[1]) != GT
&& valid_operands (IF_THEN_ELSE, operands, QFmode)"
"@
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_fmt_ee (code, VOIDmode, ccreg, const0_rtx),
operands[2], operands[3])));
DONE;}")
 
(define_insn "*ldhf_conditional"
[(set (match_operand:HF 0 "reg_operand" "=h,h")
(if_then_else:HF (match_operator 1 "comparison_operator"
[(reg:CC 21) (const_int 0)])
(match_operand:HF 2 "src_operand" "hH,0")
(match_operand:HF 3 "src_operand" "0,hH")))]
""
"@
ldf%1\\t%2,%0
ldf%I1\\t%3,%0"
[(set_attr "type" "binary")])
 
(define_insn "*ldhf_conditional_noov"
[(set (match_operand:HF 0 "reg_operand" "=h,h")
(if_then_else:HF (match_operator 1 "comparison_operator"
[(reg:CC_NOOV 21) (const_int 0)])
(match_operand:HF 2 "src_operand" "hH,0")
(match_operand:HF 3 "src_operand" "0,hH")))]
"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 "movhfcc"
[(set (match_operand:HF 0 "reg_operand" "")
(if_then_else:HF (match_operand 1 "comparison_operator" "")
(match_operand:HF 2 "src_operand" "")
(match_operand:HF 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 (HFmode, operands[0],
gen_rtx_IF_THEN_ELSE (HFmode,
gen_rtx_fmt_ee (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:QI 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:QI 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")] UNSPEC_BU))]
""
"bu%#\\t%0"
[(set_attr "type" "jump")])
 
(define_expand "caseqi"
[(parallel [(set (match_dup 5)
(minus:QI (match_operand:QI 0 "reg_operand" "")
(match_operand:QI 1 "src_operand" "")))
(clobber (reg:CC_NOOV 21))])
(set (reg:CC 21)
(compare:CC (match_dup 5)
(match_operand:QI 2 "src_operand" "")))
(set (pc)
(if_then_else (gtu (reg:CC 21)
(const_int 0))
(label_ref (match_operand 4 "" ""))
(pc)))
(parallel [(set (match_dup 6)
(plus:QI (match_dup 5)
(label_ref:QI (match_operand 3 "" ""))))
(clobber (reg:CC_NOOV 21))])
(set (match_dup 7)
(mem:QI (match_dup 6)))
(set (pc) (match_dup 7))]
""
"operands[5] = gen_reg_rtx (QImode);
operands[6] = gen_reg_rtx (QImode);
operands[7] = gen_reg_rtx (QImode);")
;
; PARALLEL FLOAT INSTRUCTIONS
;
; This patterns are under development
 
;
; ABSF/STF
;
 
(define_insn "*absqf2_movqf_clobber"
[(set (match_operand:QF 0 "ext_low_reg_operand" "=q")
(abs:QF (match_operand:QF 1 "par_ind_operand" "S<>")))
(set (match_operand:QF 2 "par_ind_operand" "=S<>")
(match_operand:QF 3 "ext_low_reg_operand" "q"))
(clobber (reg:CC_NOOV 21))]
"TARGET_PARALLEL && valid_parallel_operands_4 (operands, QFmode)"
"absf\\t%1,%0\\n||\\tstf\\t%3,%2"
[(set_attr "type" "binarycc")])
 
;
; ADDF/STF
;
 
(define_insn "*addqf3_movqf_clobber"
[(set (match_operand:QF 0 "ext_low_reg_operand" "=q,q")
(plus:QF (match_operand:QF 1 "parallel_operand" "%q,S<>")
(match_operand:QF 2 "parallel_operand" "S<>,q")))
(set (match_operand:QF 3 "par_ind_operand" "=S<>,S<>")
(match_operand:QF 4 "ext_low_reg_operand" "q,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,binarycc")])
 
;
; FLOAT/STF
;
 
(define_insn "*floatqiqf2_movqf_clobber"
[(set (match_operand:QF 0 "ext_low_reg_operand" "=q")
(float:QF (match_operand:QI 1 "par_ind_operand" "S<>")))
(set (match_operand:QF 2 "par_ind_operand" "=S<>")
(match_operand:QF 3 "ext_low_reg_operand" "q"))
(clobber (reg:CC 21))]
"TARGET_PARALLEL && valid_parallel_operands_4 (operands, QFmode)"
"float\\t%1,%0\\n||\\tstf\\t%3,%2"
[(set_attr "type" "binarycc")])
 
;
; MPYF/ADDF
;
 
(define_insn "*mulqf3_addqf3_clobber"
[(set (match_operand:QF 0 "r0r1_reg_operand" "=t,t,t,t")
(mult:QF (match_operand:QF 1 "parallel_operand" "%S<>!V,q,S<>!V,q")
(match_operand:QF 2 "parallel_operand" "q,S<>!V,S<>!V,q")))
(set (match_operand:QF 3 "r2r3_reg_operand" "=u,u,u,u")
(plus:QF (match_operand:QF 4 "parallel_operand" "%S<>!V,q,q,S<>!V")
(match_operand:QF 5 "parallel_operand" "q,S<>!V,q,S<>!V")))
(clobber (reg:CC_NOOV 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,binarycc,binarycc,binarycc")])
 
 
;
; MPYF/STF
;
 
(define_insn "*mulqf3_movqf_clobber"
[(set (match_operand:QF 0 "ext_low_reg_operand" "=q,q")
(mult:QF (match_operand:QF 1 "parallel_operand" "%q,S<>")
(match_operand:QF 2 "parallel_operand" "S<>,q")))
(set (match_operand:QF 3 "par_ind_operand" "=S<>,S<>")
(match_operand:QF 4 "ext_low_reg_operand" "q,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,binarycc")])
 
;
; MPYF/SUBF
;
 
(define_insn "*mulqf3_subqf3_clobber"
[(set (match_operand:QF 0 "r0r1_reg_operand" "=t,t")
(mult:QF (match_operand:QF 1 "parallel_operand" "S<>,q")
(match_operand:QF 2 "parallel_operand" "q,S<>")))
(set (match_operand:QF 3 "r2r3_reg_operand" "=u,u")
(minus:QF (match_operand:QF 4 "parallel_operand" "S<>,q")
(match_operand:QF 5 "parallel_operand" "q,S<>")))
(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,binarycc")])
 
;
; MPYF/LDF 0
;
 
(define_insn "*mulqf3_clrqf_clobber"
[(set (match_operand:QF 0 "r0r1_reg_operand" "=t")
(mult:QF (match_operand:QF 1 "par_ind_operand" "%S<>")
(match_operand:QF 2 "par_ind_operand" "S<>")))
(set (match_operand:QF 3 "r2r3_reg_operand" "=u")
(match_operand:QF 4 "fp_zero_operand" "G"))
(clobber (reg:CC 21))]
"TARGET_PARALLEL_MPY"
"mpyf3\\t%2,%1,%0\\n||\\tsubf3\\t%3,%3,%3"
[(set_attr "type" "binarycc")])
 
;
; NEGF/STF
;
 
(define_insn "*negqf2_movqf_clobber"
[(set (match_operand:QF 0 "ext_low_reg_operand" "=q")
(neg:QF (match_operand:QF 1 "par_ind_operand" "S<>")))
(set (match_operand:QF 2 "par_ind_operand" "=S<>")
(match_operand:QF 3 "ext_low_reg_operand" "q"))
(clobber (reg:CC 21))]
"TARGET_PARALLEL && valid_parallel_operands_4 (operands, QFmode)"
"negf\\t%1,%0\\n||\\tstf\\t%3,%2"
[(set_attr "type" "binarycc")])
 
;
; SUBF/STF
;
 
(define_insn "*subqf3_movqf_clobber"
[(set (match_operand:QF 0 "ext_low_reg_operand" "=q")
(minus:QF (match_operand:QF 1 "ext_low_reg_operand" "q")
(match_operand:QF 2 "par_ind_operand" "S<>")))
(set (match_operand:QF 3 "par_ind_operand" "=S<>")
(match_operand:QF 4 "ext_low_reg_operand" "q"))
(clobber (reg:CC 21))]
"TARGET_PARALLEL && valid_parallel_operands_5 (operands, QFmode)"
"subf3\\t%2,%1,%0\\n||\\tstf\\t%4,%3"
[(set_attr "type" "binarycc")])
 
;
; TOIEEE/STF
;
 
(define_insn "*toieee_movqf_clobber"
[(set (match_operand:QF 0 "ext_low_reg_operand" "=q")
(unspec:QF [(match_operand:QF 1 "par_ind_operand" "S<>")] UNSPEC_TOIEEE))
(set (match_operand:QF 2 "par_ind_operand" "=S<>")
(match_operand:QF 3 "ext_low_reg_operand" "q"))
(clobber (reg:CC 21))]
"TARGET_PARALLEL && valid_parallel_operands_4 (operands, QFmode)"
"toieee\\t%1,%0\\n||\\tstf\\t%3,%2"
[(set_attr "type" "binarycc")])
 
;
; FRIEEE/STF
;
 
(define_insn "*frieee_movqf_clobber"
[(set (match_operand:QF 0 "ext_low_reg_operand" "=q")
(unspec:QF [(match_operand:QF 1 "par_ind_operand" "S<>")] UNSPEC_FRIEEE))
(set (match_operand:QF 2 "par_ind_operand" "=S<>")
(match_operand:QF 3 "ext_low_reg_operand" "q"))
(clobber (reg:CC 21))]
"TARGET_PARALLEL && valid_parallel_operands_4 (operands, QFmode)"
"frieee\\t%1,%0\\n||\\tstf\\t%3,%2"
[(set_attr "type" "binarycc")])
 
;
; PARALLEL INTEGER INSTRUCTIONS
;
 
;
; ABSI/STI
;
 
(define_insn "*absqi2_movqi_clobber"
[(set (match_operand:QI 0 "ext_low_reg_operand" "=q")
(abs:QI (match_operand:QI 1 "par_ind_operand" "S<>")))
(set (match_operand:QI 2 "par_ind_operand" "=S<>")
(match_operand:QI 3 "ext_low_reg_operand" "q"))
(clobber (reg:CC_NOOV 21))]
"TARGET_PARALLEL && valid_parallel_operands_4 (operands, QImode)"
"absi\\t%1,%0\\n||\\tsti\\t%3,%2"
[(set_attr "type" "binarycc")])
 
;
; ADDI/STI
;
 
(define_insn "*addqi3_movqi_clobber"
[(set (match_operand:QI 0 "ext_low_reg_operand" "=q,q")
(plus:QI (match_operand:QI 1 "parallel_operand" "%q,S<>")
(match_operand:QI 2 "parallel_operand" "S<>,q")))
(set (match_operand:QI 3 "par_ind_operand" "=S<>,S<>")
(match_operand:QI 4 "ext_low_reg_operand" "q,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,binarycc")])
 
;
; AND/STI
;
 
(define_insn "*andqi3_movqi_clobber"
[(set (match_operand:QI 0 "ext_low_reg_operand" "=q,q")
(and:QI (match_operand:QI 1 "parallel_operand" "%q,S<>")
(match_operand:QI 2 "parallel_operand" "S<>,q")))
(set (match_operand:QI 3 "par_ind_operand" "=S<>,S<>")
(match_operand:QI 4 "ext_low_reg_operand" "q,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,binarycc")])
 
;
; ASH(left)/STI
;
 
(define_insn "*ashlqi3_movqi_clobber"
[(set (match_operand:QI 0 "ext_low_reg_operand" "=q")
(ashift:QI (match_operand:QI 1 "par_ind_operand" "S<>")
(match_operand:QI 2 "ext_low_reg_operand" "q")))
(set (match_operand:QI 3 "par_ind_operand" "=S<>")
(match_operand:QI 4 "ext_low_reg_operand" "q"))
(clobber (reg:CC 21))]
"TARGET_PARALLEL && valid_parallel_operands_5 (operands, QImode)"
"ash3\\t%2,%1,%0\\n||\\tsti\\t%4,%3"
[(set_attr "type" "binarycc")])
 
;
; ASH(right)/STI
;
 
(define_insn "*ashrqi3_movqi_clobber"
[(set (match_operand:QI 0 "ext_low_reg_operand" "=q")
(ashiftrt:QI (match_operand:QI 1 "par_ind_operand" "S<>")
(neg:QI (match_operand:QI 2 "ext_low_reg_operand" "q"))))
(set (match_operand:QI 3 "par_ind_operand" "=S<>")
(match_operand:QI 4 "ext_low_reg_operand" "q"))
(clobber (reg:CC 21))]
"TARGET_PARALLEL && valid_parallel_operands_5 (operands, QImode)"
"ash3\\t%2,%1,%0\\n||\\tsti\\t%4,%3"
[(set_attr "type" "binarycc")])
 
;
; FIX/STI
;
 
(define_insn "*fixqfqi2_movqi_clobber"
[(set (match_operand:QI 0 "ext_low_reg_operand" "=q")
(fix:QI (match_operand:QF 1 "par_ind_operand" "S<>")))
(set (match_operand:QI 2 "par_ind_operand" "=S<>")
(match_operand:QI 3 "ext_low_reg_operand" "q"))
(clobber (reg:CC 21))]
"TARGET_PARALLEL && valid_parallel_operands_4 (operands, QImode)"
"fix\\t%1,%0\\n||\\tsti\\t%3,%2"
[(set_attr "type" "binarycc")])
 
;
; LSH(right)/STI
;
 
(define_insn "*lshrqi3_movqi_clobber"
[(set (match_operand:QI 0 "ext_low_reg_operand" "=q")
(lshiftrt:QI (match_operand:QI 1 "par_ind_operand" "S<>")
(neg:QI (match_operand:QI 2 "ext_low_reg_operand" "q"))))
(set (match_operand:QI 3 "par_ind_operand" "=S<>")
(match_operand:QI 4 "ext_low_reg_operand" "q"))
(clobber (reg:CC 21))]
"TARGET_PARALLEL && valid_parallel_operands_5 (operands, QImode)"
"lsh3\\t%2,%1,%0\\n||\\tsti\\t%4,%3"
[(set_attr "type" "binarycc")])
 
;
; MPYI/ADDI
;
 
(define_insn "*mulqi3_addqi3_clobber"
[(set (match_operand:QI 0 "r0r1_reg_operand" "=t,t,t,t")
(mult:QI (match_operand:QI 1 "parallel_operand" "%S<>!V,q,S<>!V,q")
(match_operand:QI 2 "parallel_operand" "q,S<>!V,S<>!V,q")))
(set (match_operand:QI 3 "r2r3_reg_operand" "=u,u,u,u")
(plus:QI (match_operand:QI 4 "parallel_operand" "%S<>!V,q,q,S<>!V")
(match_operand:QI 5 "parallel_operand" "q,S<>!V,q,S<>!V")))
(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,binarycc,binarycc,binarycc")])
 
 
;
; MPYI/STI
;
 
(define_insn "*mulqi3_movqi_clobber"
[(set (match_operand:QI 0 "ext_low_reg_operand" "=q,q")
(mult:QI (match_operand:QI 1 "parallel_operand" "%q,S<>")
(match_operand:QI 2 "parallel_operand" "S<>,q")))
(set (match_operand:QI 3 "par_ind_operand" "=S<>,S<>")
(match_operand:QI 4 "ext_low_reg_operand" "q,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,binarycc")])
 
;
; MPYI/SUBI
;
 
(define_insn "*mulqi3_subqi3_clobber"
[(set (match_operand:QI 0 "r0r1_reg_operand" "=t,t")
(mult:QI (match_operand:QI 1 "parallel_operand" "S<>,q")
(match_operand:QI 2 "parallel_operand" "q,S<>")))
(set (match_operand:QI 3 "r2r3_reg_operand" "=u,u")
(minus:QI (match_operand:QI 4 "parallel_operand" "S<>,q")
(match_operand:QI 5 "parallel_operand" "q,S<>")))
(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,binarycc")])
 
;
; MPYI/LDI 0
;
 
(define_insn "*mulqi3_clrqi_clobber"
[(set (match_operand:QI 0 "r0r1_reg_operand" "=t")
(mult:QI (match_operand:QI 1 "par_ind_operand" "%S<>")
(match_operand:QI 2 "par_ind_operand" "S<>")))
(set (match_operand:QI 3 "r2r3_reg_operand" "=u")
(const_int 0))
(clobber (reg:CC 21))]
"TARGET_PARALLEL_MPY && TARGET_MPYI"
"mpyi3\\t%2,%1,%0\\n||\\tsubi3\\t%3,%3,%3"
[(set_attr "type" "binarycc")])
 
;
; NEGI/STI
;
 
(define_insn "*negqi2_movqi_clobber"
[(set (match_operand:QI 0 "ext_low_reg_operand" "=q")
(neg:QI (match_operand:QI 1 "par_ind_operand" "S<>")))
(set (match_operand:QI 2 "par_ind_operand" "=S<>")
(match_operand:QI 3 "ext_low_reg_operand" "q"))
(clobber (reg:CC 21))]
"TARGET_PARALLEL && valid_parallel_operands_4 (operands, QImode)"
"negi\\t%1,%0\\n||\\tsti\\t%3,%2"
[(set_attr "type" "binarycc")])
 
;
; NOT/STI
;
 
(define_insn "*notqi2_movqi_clobber"
[(set (match_operand:QI 0 "ext_low_reg_operand" "=q")
(not:QI (match_operand:QI 1 "par_ind_operand" "S<>")))
(set (match_operand:QI 2 "par_ind_operand" "=S<>")
(match_operand:QI 3 "ext_low_reg_operand" "q"))
(clobber (reg:CC 21))]
"TARGET_PARALLEL && valid_parallel_operands_4 (operands, QImode)"
"not\\t%1,%0\\n||\\tsti\\t%3,%2"
[(set_attr "type" "binarycc")])
 
;
; OR/STI
;
 
(define_insn "*iorqi3_movqi_clobber"
[(set (match_operand:QI 0 "ext_low_reg_operand" "=q,q")
(ior:QI (match_operand:QI 1 "parallel_operand" "%q,S<>")
(match_operand:QI 2 "parallel_operand" "S<>,q")))
(set (match_operand:QI 3 "par_ind_operand" "=S<>,S<>")
(match_operand:QI 4 "ext_low_reg_operand" "q,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,binarycc")])
 
;
; SUBI/STI
;
 
(define_insn "*subqi3_movqi_clobber"
[(set (match_operand:QI 0 "ext_low_reg_operand" "=q")
(minus:QI (match_operand:QI 1 "par_ind_operand" "S<>")
(match_operand:QI 2 "ext_low_reg_operand" "q")))
(set (match_operand:QI 3 "par_ind_operand" "=S<>")
(match_operand:QI 4 "ext_low_reg_operand" "q"))
(clobber (reg:CC 21))]
"TARGET_PARALLEL && valid_parallel_operands_5 (operands, QImode)"
"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,q")
(xor:QI (match_operand:QI 1 "parallel_operand" "%q,S<>")
(match_operand:QI 2 "parallel_operand" "S<>,q")))
(set (match_operand:QI 3 "par_ind_operand" "=S<>,S<>")
(match_operand:QI 4 "ext_low_reg_operand" "q,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,binarycc")])
 
;
; BRANCH/CALL INSTRUCTIONS
;
 
;
; Branch instructions
;
(define_insn "*b"
[(set (pc) (if_then_else (match_operator 0 "comparison_operator"
[(reg:CC 21) (const_int 0)])
(label_ref (match_operand 1 "" ""))
(pc)))]
""
"*
return c4x_output_cbranch (\"b%0\", insn);"
[(set_attr "type" "jmpc")])
 
(define_insn "*b_rev"
[(set (pc) (if_then_else (match_operator 0 "comparison_operator"
[(reg:CC 21) (const_int 0)])
(pc)
(label_ref (match_operand 1 "" ""))))]
""
"*
return c4x_output_cbranch (\"b%I0\", insn);"
[(set_attr "type" "jmpc")])
 
(define_insn "*b_noov"
[(set (pc) (if_then_else (match_operator 0 "comparison_operator"
[(reg:CC_NOOV 21) (const_int 0)])
(label_ref (match_operand 1 "" ""))
(pc)))]
"GET_CODE (operands[0]) != LE
&& GET_CODE (operands[0]) != GE
&& GET_CODE (operands[0]) != LT
&& GET_CODE (operands[0]) != GT"
"*
return c4x_output_cbranch (\"b%0\", insn);"
[(set_attr "type" "jmpc")])
 
(define_insn "*b_noov_rev"
[(set (pc) (if_then_else (match_operator 0 "comparison_operator"
[(reg:CC_NOOV 21) (const_int 0)])
(pc)
(label_ref (match_operand 1 "" ""))))]
"GET_CODE (operands[0]) != LE
&& GET_CODE (operands[0]) != GE
&& GET_CODE (operands[0]) != LT
&& GET_CODE (operands[0]) != GT"
"*
return c4x_output_cbranch (\"b%I0\", insn);"
[(set_attr "type" "jmpc")])
 
(define_expand "beq"
[(set (pc) (if_then_else (eq (match_dup 1) (const_int 0))
(label_ref (match_operand 0 "" ""))
(pc)))]
""
"operands[1] = c4x_gen_compare_reg (EQ, c4x_compare_op0, c4x_compare_op1);")
 
(define_expand "bne"
[(set (pc) (if_then_else (ne (match_dup 1) (const_int 0))
(label_ref (match_operand 0 "" ""))
(pc)))]
""
"operands[1] = c4x_gen_compare_reg (NE, c4x_compare_op0, c4x_compare_op1);")
 
(define_expand "blt"
[(set (pc) (if_then_else (lt (match_dup 1) (const_int 0))
(label_ref (match_operand 0 "" ""))
(pc)))]
""
"operands[1] = c4x_gen_compare_reg (LT, c4x_compare_op0, c4x_compare_op1);
if (operands[1] == NULL_RTX) FAIL;")
 
(define_expand "bltu"
[(set (pc) (if_then_else (ltu (match_dup 1) (const_int 0))
(label_ref (match_operand 0 "" ""))
(pc)))]
""
"operands[1] = c4x_gen_compare_reg (LTU, c4x_compare_op0, c4x_compare_op1);")
 
(define_expand "bgt"
[(set (pc) (if_then_else (gt (match_dup 1) (const_int 0))
(label_ref (match_operand 0 "" ""))
(pc)))]
""
"operands[1] = c4x_gen_compare_reg (GT, c4x_compare_op0, c4x_compare_op1);
if (operands[1] == NULL_RTX) FAIL;")
 
(define_expand "bgtu"
[(set (pc) (if_then_else (gtu (match_dup 1) (const_int 0))
(label_ref (match_operand 0 "" ""))
(pc)))]
""
"operands[1] = c4x_gen_compare_reg (GTU, c4x_compare_op0, c4x_compare_op1);")
 
(define_expand "ble"
[(set (pc) (if_then_else (le (match_dup 1) (const_int 0))
(label_ref (match_operand 0 "" ""))
(pc)))]
""
"operands[1] = c4x_gen_compare_reg (LE, c4x_compare_op0, c4x_compare_op1);
if (operands[1] == NULL_RTX) FAIL;")
 
(define_expand "bleu"
[(set (pc) (if_then_else (leu (match_dup 1) (const_int 0))
(label_ref (match_operand 0 "" ""))
(pc)))]
""
"operands[1] = c4x_gen_compare_reg (LEU, c4x_compare_op0, c4x_compare_op1);")
 
(define_expand "bge"
[(set (pc) (if_then_else (ge (match_dup 1) (const_int 0))
(label_ref (match_operand 0 "" ""))
(pc)))]
""
"operands[1] = c4x_gen_compare_reg (GE, c4x_compare_op0, c4x_compare_op1);
if (operands[1] == NULL_RTX) FAIL;")
 
(define_expand "bgeu"
[(set (pc) (if_then_else (geu (match_dup 1) (const_int 0))
(label_ref (match_operand 0 "" ""))
(pc)))]
""
"operands[1] = c4x_gen_compare_reg (GEU, c4x_compare_op0, c4x_compare_op1);")
 
(define_insn "*b_reg"
[(set (pc) (match_operand:QI 0 "reg_operand" "r"))]
""
"bu%#\\t%0"
[(set_attr "type" "jump")])
 
(define_expand "indirect_jump"
[(set (pc) (match_operand:QI 0 "reg_operand" ""))]
""
"")
 
(define_insn "tablejump"
[(set (pc) (match_operand:QI 0 "src_operand" "r"))
(use (label_ref (match_operand 1 "" "")))]
""
"bu%#\\t%0"
[(set_attr "type" "jump")])
 
;
; CALL
;
(define_insn "*call_c3x"
[(call (mem:QI (match_operand:QI 0 "call_address_operand" "Ur"))
(match_operand:QI 1 "general_operand" ""))
(clobber (reg:QI 31))]
;; Operand 1 not really used on the C4x. The C30 doesn't have reg 31.
 
"TARGET_C3X"
"call%U0\\t%C0"
[(set_attr "type" "call")])
 
; LAJ requires R11 (31) for the return address
(define_insn "*laj"
[(call (mem:QI (match_operand:QI 0 "call_address_operand" "Ur"))
(match_operand:QI 1 "general_operand" ""))
(clobber (reg:QI 31))]
;; Operand 1 not really used on the C4x.
 
"! TARGET_C3X"
"*
if (final_sequence)
return c4x_check_laj_p (insn)
? \"nop\\n\\tlaj%U0\\t%C0\" : \"laj%U0\\t%C0\";
else
return \"call%U0\\t%C0\";"
[(set_attr "type" "laj")])
 
(define_expand "call"
[(parallel [(call (match_operand:QI 0 "" "")
(match_operand:QI 1 "general_operand" ""))
(clobber (reg:QI 31))])]
""
"
{
if (GET_CODE (operands[0]) == MEM
&& ! call_address_operand (XEXP (operands[0], 0), Pmode))
operands[0] = gen_rtx_MEM (GET_MODE (operands[0]),
force_reg (Pmode, XEXP (operands[0], 0)));
}")
 
(define_insn "nodb_call"
[(call (mem:QI (match_operand:QI 0 "call_address_operand" "Ur"))
(const_int 0))]
""
"call%U0\\t%C0"
[(set_attr "type" "call")])
 
(define_insn "*callv_c3x"
[(set (match_operand 0 "" "=r")
(call (mem:QI (match_operand:QI 1 "call_address_operand" "Ur"))
(match_operand:QI 2 "general_operand" "")))
(clobber (reg:QI 31))]
;; Operand 0 and 2 not really used for the C4x.
;; The C30 doesn't have reg 31.
 
"TARGET_C3X"
"call%U1\\t%C1"
[(set_attr "type" "call")])
 
; LAJ requires R11 (31) for the return address
(define_insn "*lajv"
[(set (match_operand 0 "" "=r")
(call (mem:QI (match_operand:QI 1 "call_address_operand" "Ur"))
(match_operand:QI 2 "general_operand" "")))
(clobber (reg:QI 31))]
;; Operand 0 and 2 not really used in the C30 instruction.
 
"! TARGET_C3X"
"*
if (final_sequence)
return c4x_check_laj_p (insn)
? \"nop\\n\\tlaj%U1\\t%C1\" : \"laj%U1\\t%C1\";
else
return \"call%U1\\t%C1\";"
[(set_attr "type" "laj")])
 
(define_expand "call_value"
[(parallel [(set (match_operand 0 "" "")
(call (match_operand:QI 1 "" "")
(match_operand:QI 2 "general_operand" "")))
(clobber (reg:QI 31))])]
""
"
{
if (GET_CODE (operands[0]) == MEM
&& ! call_address_operand (XEXP (operands[1], 0), Pmode))
operands[0] = gen_rtx_MEM (GET_MODE (operands[1]),
force_reg (Pmode, XEXP (operands[1], 0)));
}")
 
(define_insn "return"
[(return)]
"! c4x_null_epilogue_p ()"
"rets"
[(set_attr "type" "rets")])
 
(define_insn "return_from_epilogue"
[(return)]
"reload_completed && ! c4x_interrupt_function_p ()"
"rets"
[(set_attr "type" "rets")])
 
(define_insn "return_from_interrupt_epilogue"
[(return)]
"reload_completed && c4x_interrupt_function_p ()"
"reti"
[(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")])
 
(define_insn "trap"
[(trap_if (const_int 1) (const_int 31))]
""
"trapu\\t31"
[(set_attr "type" "call")])
 
(define_expand "conditional_trap"
[(trap_if (match_operand 0 "comparison_operator" "")
(match_operand 1 "const_int_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;
if (GET_MODE (ccreg) == CCmode)
emit_insn (gen_cond_trap_cc (operands[0], operands[1]));
else
emit_insn (gen_cond_trap_cc_noov (operands[0], operands[1]));
DONE;}")
 
(define_insn "cond_trap_cc"
[(trap_if (match_operator 0 "comparison_operator"
[(reg:CC 21) (const_int 0)])
(match_operand 1 "const_int_operand" ""))]
""
"trap%0\\t31"
[(set_attr "type" "call")])
 
(define_insn "cond_trap_cc_noov"
[(trap_if (match_operator 0 "comparison_operator"
[(reg:CC_NOOV 21) (const_int 0)])
(match_operand 1 "const_int_operand" ""))]
"GET_CODE (operands[0]) != LE
&& GET_CODE (operands[0]) != GE
&& GET_CODE (operands[0]) != LT
&& GET_CODE (operands[0]) != GT"
"trap%0\\t31"
[(set_attr "type" "call")])
 
;
; 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
; utilize this instruction. However, if there are more than one
; memory reference in the loop, it cannot guarantee that reversing
; the loop will work :( (see check_dbra_loop() in loop.c)
; Note that the C3x only decrements the 24 LSBs of the address register
; and the 8 MSBs are untouched. The C4x uses all 32-bits. We thus
; have an option to disable this instruction.
(define_insn "*db"
[(set (pc)
(if_then_else (ne (match_operand:QI 0 "addr_reg_operand" "+a,?*d,??*r,!m")
(const_int 0))
(label_ref (match_operand 1 "" ""))
(pc)))
(set (match_dup 0)
(plus:QI (match_dup 0)
(const_int -1)))
(use (reg:QI 20))
(clobber (reg:CC_NOOV 21))]
"TARGET_DB && TARGET_LOOP_UNSIGNED"
"*
if (which_alternative == 0)
return \"dbu%#\\t%0,%l1\";
else if (which_alternative == 1)
return c4x_output_cbranch (\"subi\\t1,%0\\n\\tbge\", insn);
else if (which_alternative == 2)
return c4x_output_cbranch (\"subi\\t1,%0\\n\\tcmpi\\t0,%0\\n\\tbge\", insn);
else
return c4x_output_cbranch (\"push\\tr0\\n\\tldi\\t%0,r0\\n\\tsubi\\t1,r0\\n\\tsti\\tr0,%0\\n\\tpop\\tr0\\n\\tbhs\", insn);
"
[(set_attr "type" "db,jmpc,jmpc,jmpc")])
 
(define_insn "*db_noclobber"
[(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)))]
"reload_completed && TARGET_DB && TARGET_LOOP_UNSIGNED"
"dbu%#\\t%0,%l1"
[(set_attr "type" "db")])
 
(define_split
[(set (pc)
(if_then_else (ne (match_operand:QI 0 "addr_reg_operand" "")
(const_int 0))
(label_ref (match_operand 1 "" ""))
(pc)))
(set (match_dup 0)
(plus:QI (match_dup 0)
(const_int -1)))
(use (reg:QI 20))
(clobber (reg:CC_NOOV 21))]
"reload_completed && TARGET_DB && TARGET_LOOP_UNSIGNED"
[(parallel [(set (pc)
(if_then_else (ne (match_dup 0)
(const_int 0))
(label_ref (match_dup 1))
(pc)))
(set (match_dup 0)
(plus:QI (match_dup 0)
(const_int -1)))])]
"")
 
; This insn is used for some loop tests, typically loops reversed when
; strength reduction is used. It is actually created when the instruction
; combination phase combines the special loop test. Since this insn
; is both a jump insn and has an output, it must deal with its own
; reloads, hence the `m' constraints.
 
; The C4x does the decrement and then compares the result against zero.
; It branches if the result was greater than or equal to zero.
; In the RTL the comparison and decrement are assumed to happen
; at the same time so we bias the iteration counter with by -1
; when we make the test.
(define_insn "decrement_and_branch_until_zero"
[(set (pc)
(if_then_else (ge (plus:QI (match_operand:QI 0 "addr_reg_operand" "+a,?*d,??*r,!m")
(const_int -1))
(const_int 0))
(label_ref (match_operand 1 "" ""))
(pc)))
(set (match_dup 0)
(plus:QI (match_dup 0)
(const_int -1)))
(use (reg:QI 20))
(clobber (reg:CC_NOOV 21))]
"TARGET_DB && (find_reg_note (insn, REG_NONNEG, 0) || TARGET_LOOP_UNSIGNED)"
"*
if (which_alternative == 0)
return \"dbu%#\\t%0,%l1\";
else if (which_alternative == 1)
return c4x_output_cbranch (\"subi\\t1,%0\\n\\tbge\", insn);
else if (which_alternative == 2)
return c4x_output_cbranch (\"subi\\t1,%0\\n\\tcmpi\\t0,%0\\n\\tbge\", insn);
else
return c4x_output_cbranch (\"push\\tr0\\n\\tldi\\t%0,r0\\n\\tsubi\\t1,r0\\n\\tsti\\tr0,%0\\n\\tpop\\tr0\\n\\tbhs\", insn);
"
[(set_attr "type" "db,jmpc,jmpc,jmpc")])
 
(define_insn "*decrement_and_branch_until_zero_noclobber"
[(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)))]
"reload_completed && TARGET_DB && TARGET_LOOP_UNSIGNED"
"dbu%#\\t%0,%l1"
[(set_attr "type" "db")])
 
(define_split
[(set (pc)
(if_then_else (ge (plus:QI (match_operand:QI 0 "addr_reg_operand" "")
(const_int -1))
(const_int 0))
(label_ref (match_operand 1 "" ""))
(pc)))
(set (match_dup 0)
(plus:QI (match_dup 0)
(const_int -1)))
(use (reg:QI 20))
(clobber (reg:CC_NOOV 21))]
"reload_completed && TARGET_DB && TARGET_LOOP_UNSIGNED"
[(parallel [(set (pc)
(if_then_else (ge (plus:QI (match_dup 0)
(const_int -1))
(const_int 0))
(label_ref (match_dup 1))
(pc)))
(set (match_dup 0)
(plus:QI (match_dup 0)
(const_int -1)))])]
"")
 
;
; MISC INSTRUCTIONS
;
 
;
; NOP
;
(define_insn "nop"
[(const_int 0)]
""
"nop")
; Default to misc type attr.
 
(define_insn "return_indirect_internal"
[(return)
(use (match_operand:QI 0 "reg_operand" ""))]
"reload_completed"
"bu%#\\t%0"
[(set_attr "type" "jump")])
 
(define_expand "prologue"
[(const_int 1)]
""
"c4x_expand_prologue (); DONE;")
 
(define_expand "epilogue"
[(const_int 1)]
""
"c4x_expand_epilogue (); DONE;")
 
;
; RPTB
;
(define_insn "rptb_top"
[(use (label_ref (match_operand 0 "" "")))
(use (label_ref (match_operand 1 "" "")))
(clobber (reg:QI 25))
(clobber (reg:QI 26))]
""
"*
return ! final_sequence && c4x_rptb_rpts_p (insn, operands[0])
? \"rpts\\trc\" : \"rptb%#\\t%l1-1\";
"
[(set_attr "type" "repeat_top")])
 
(define_insn "rpts_top"
[(unspec [(use (label_ref (match_operand 0 "" "")))
(use (label_ref (match_operand 1 "" "")))] UNSPEC_RPTS)
(clobber (reg:QI 25))
(clobber (reg:QI 26))]
""
"*
return ! final_sequence && c4x_rptb_rpts_p (insn, operands[0])
? \"rpts\\trc\" : \"rptb%#\\t%l1-1\";
"
[(set_attr "type" "repeat")])
 
; This pattern needs to be emitted at the start of the loop to
; say that RS and RE are loaded.
(define_insn "rptb_init"
[(unspec [(match_operand:QI 0 "register_operand" "va")] UNSPEC_RPTB_INIT)
(clobber (reg:QI 25))
(clobber (reg:QI 26))]
""
""
[(set_attr "type" "repeat")])
 
 
; operand 0 is the loop count pseudo register
; operand 1 is the number of loop iterations or 0 if it is unknown
; operand 2 is the maximum number of loop iterations
; operand 3 is the number of levels of enclosed loops
(define_expand "doloop_begin"
[(use (match_operand 0 "register_operand" ""))
(use (match_operand:QI 1 "const_int_operand" ""))
(use (match_operand:QI 2 "const_int_operand" ""))
(use (match_operand:QI 3 "const_int_operand" ""))]
""
"if (INTVAL (operands[3]) > 1 || ! TARGET_RPTB)
FAIL;
emit_insn (gen_rptb_init (operands[0]));
DONE;
")
 
 
; The RS (25) and RE (26) registers must be unviolate from the top of the loop
; to here.
(define_insn "rptb_end"
[(set (pc)
(if_then_else (ge (match_operand:QI 0 "register_operand" "+v,?a,!*d,!*x*k,!m")
(const_int 0))
(label_ref (match_operand 1 "" ""))
(pc)))
(set (match_dup 0)
(plus:QI (match_dup 0)
(const_int -1)))
(use (reg:QI 25))
(use (reg:QI 26))
(use (reg:QI 20))
(clobber (reg:CC_NOOV 21))]
""
"*
if (which_alternative == 0)
return c4x_rptb_nop_p (insn) ? \"nop\" : \"\";
else if (which_alternative == 1 && TARGET_DB)
return \"dbu%#\\t%0,%l1\";
else if (which_alternative == 2)
return c4x_output_cbranch (\"subi\\t1,%0\\n\\tbge\", insn);
else if (which_alternative == 3 || (which_alternative == 1 && ! TARGET_DB))
return c4x_output_cbranch (\"subi\\t1,%0\\n\\tcmpi\\t0,%0\\n\\tbge\", insn);
else
return c4x_output_cbranch (\"push\\tr0\\n\\tldi\\t%0,r0\\n\\tsubi\\t1,r0\\n\\tsti\\tr0,%0\\n\\tpop\\tr0\\n\\tbhs\", insn);
"
[(set_attr "type" "repeat,db,jmpc,jmpc,jmpc")])
 
(define_split
[(set (pc)
(if_then_else (ge (match_operand:QI 0 "addr_reg_operand" "")
(const_int 0))
(label_ref (match_operand 1 "" ""))
(pc)))
(set (match_dup 0)
(plus:QI (match_dup 0)
(const_int -1)))
(use (match_operand:QI 2 "const_int_operand" ""))
(use (match_operand:QI 3 "const_int_operand" ""))
(use (match_operand:QI 4 "const_int_operand" ""))
(use (reg:QI 25))
(use (reg:QI 26))
(use (reg:QI 20))
(clobber (reg:CC_NOOV 21))]
"reload_completed"
[(parallel [(set (pc)
(if_then_else (ge (match_dup 0)
(const_int 0))
(label_ref (match_dup 1))
(pc)))
(set (match_dup 0)
(plus:QI (match_dup 0)
(const_int -1)))])]
"")
 
; operand 0 is the loop count pseudo register
; operand 1 is the number of loop iterations or 0 if it is unknown
; operand 2 is the maximum number of loop iterations
; operand 3 is the number of levels of enclosed loops
; operand 4 is the label to jump to at the top of the loop
(define_expand "doloop_end"
[(use (match_operand 0 "register_operand" ""))
(use (match_operand:QI 1 "const_int_operand" ""))
(use (match_operand:QI 2 "const_int_operand" ""))
(use (match_operand:QI 3 "const_int_operand" ""))
(use (label_ref (match_operand 4 "" "")))]
""
"if (! TARGET_LOOP_UNSIGNED
&& (unsigned HOST_WIDE_INT) INTVAL (operands[2]) > ((unsigned) 1 << 31))
FAIL;
if (INTVAL (operands[3]) > 1 || ! TARGET_RPTB)
{
/* The C30 maximum iteration count for DB is 2^24. */
if (! TARGET_DB)
FAIL;
emit_jump_insn (gen_decrement_and_branch_until_zero (operands[0],
operands[4]));
DONE;
}
emit_jump_insn (gen_rptb_end (operands[0], operands[4]));
DONE;
")
 
(define_expand "decrement_and_branch_on_count"
[(parallel [(set (pc)
(if_then_else (ge (match_operand:QI 0 "register_operand" "")
(const_int 0))
(label_ref (match_operand 1 "" ""))
(pc)))
(set (match_dup 0)
(plus:QI (match_dup 0)
(const_int -1)))
(use (reg:QI 25))
(use (reg:QI 26))
(clobber (reg:CC_NOOV 21))])]
"0"
"")
 
(define_expand "movmemqi_small"
[(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);
 
if (TARGET_PARALLEL)
{
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));
}
else
{
for (i = 0; i < len; i++)
{
emit_insn (gen_movqi (tmp, src_mem));
emit_insn (gen_movqi (dst_mem, tmp));
emit_insn (gen_addqi3_noclobber (src, src, const1_rtx));
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
; an 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 "movmemqi_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_scratch:QI 5 "=0"))
(clobber (match_scratch:QI 6 "=1"))
(clobber (reg:QI 25))
(clobber (reg:QI 26))
(clobber (reg:QI 27))]
""
"*
{
int i;
int len = INTVAL (operands[2]);
 
output_asm_insn (\"ldiu\\t*%1++,%4\", operands);
if (len < 8)
{
for (i = 1; i < len; i++)
{
output_asm_insn (\"sti\\t%4,*%0++\", operands);
output_asm_insn (\"|| ldi\\t*%1++,%4\", operands);
}
}
else
{
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);
}
else
{
output_asm_insn (\"ldiu\\t%2-2,rc\", operands);
output_asm_insn (\"rptb\\t$+1\", operands);
output_asm_insn (\"sti\\t%4,*%0++\", operands);
output_asm_insn (\"|| ldi\\t*%1++,%4\", operands);
}
}
return \"sti\\t%4,*%0++\";
}"
[(set_attr "type" "multi")])
 
; Operand 2 is the count, operand 3 is the alignment.
(define_expand "movmemqi"
[(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);
/* Disabled because of reload problems. */
if (0 && INTVAL (operands[2]) < 8)
emit_insn (gen_movmemqi_small (operands[0], operands[1], operands[2],
operands[3], tmp));
else
{
emit_insn (gen_movmemqi_large (operands[0], operands[1], operands[2],
operands[3], tmp));
}
DONE;
}")
 
 
(define_insn "*cmpstrnqi"
[(set (match_operand:QI 0 "ext_reg_operand" "=d")
(compare:QI (mem:BLK (match_operand:QI 1 "addr_reg_operand" "+a"))
(mem:BLK (match_operand:QI 2 "addr_reg_operand" "+a"))))
(use (match_operand:QI 3 "immediate_operand" "i"))
(use (match_operand:QI 4 "immediate_operand" ""))
(clobber (match_operand:QI 5 "std_reg_operand" "=&c"))
(clobber (reg:QI 21))]
""
"*
{
output_asm_insn (\"ldi\\t%3-1,%5\", operands);
output_asm_insn (\"$1:\tsubi3\\t*%1++,*%2++,%0\", operands);
output_asm_insn (\"dbeq\\t%5,$1\", operands);
return \"\";
}")
 
(define_expand "cmpstrnqi"
[(parallel [(set (match_operand:QI 0 "reg_operand" "")
(compare:QI (match_operand:BLK 1 "general_operand" "")
(match_operand:BLK 2 "general_operand" "")))
(use (match_operand:QI 3 "immediate_operand" ""))
(use (match_operand:QI 4 "immediate_operand" ""))
(clobber (match_dup 5))
(clobber (reg:QI 21))])]
""
"
{
if (GET_CODE (operands[3]) != CONST_INT
|| INTVAL (operands[3]) > 32767
|| INTVAL (operands[3]) <= 0)
{
FAIL;
}
operands[1] = copy_to_mode_reg (Pmode, XEXP (operands[1], 0));
operands[2] = copy_to_mode_reg (Pmode, XEXP (operands[2], 0));
operands[5] = gen_reg_rtx (QImode);
}")
 
;
; TWO OPERAND LONG DOUBLE INSTRUCTIONS
;
 
(define_expand "movhf"
[(set (match_operand:HF 0 "src_operand" "")
(match_operand:HF 1 "src_operand" ""))]
""
"if (c4x_emit_move_sequence (operands, HFmode))
DONE;")
 
(define_insn "*movhf_noclobber_reg"
[(set (match_operand:HF 0 "reg_operand" "=h")
(match_operand:HF 1 "src_operand" "Hh"))]
"GET_CODE (operands[1]) != MEM"
"ldfu\\t%1,%0"
[(set_attr "type" "unary")])
 
(define_insn "*movhf_noclobber"
[(set (match_operand:HF 0 "dst_operand" "=h,m")
(match_operand:HF 1 "src_operand" "Hm,h"))]
"reg_operand (operands[0], HFmode) ^ reg_operand (operands[1], HFmode)"
"#"
[(set_attr "type" "multi,multi")])
 
(define_insn "*movhf_test"
[(set (reg:CC 21)
(compare:CC (match_operand:HF 1 "reg_operand" "h")
(const_int 0)))
(clobber (match_scratch:HF 0 "=h"))]
""
"ldf\\t%1,%0"
[(set_attr "type" "unarycc")])
 
(define_insn "*movhf_set"
[(set (reg:CC 21)
(compare:CC (match_operand:HF 1 "reg_operand" "h")
(match_operand:HF 2 "fp_zero_operand" "G")))
(set (match_operand:HF 0 "reg_operand" "=h")
(match_dup 1))]
""
"ldf\\t%1,%0"
[(set_attr "type" "unarycc")])
 
(define_split
[(set (match_operand:HF 0 "reg_operand" "")
(match_operand:HF 1 "memory_operand" ""))]
"reload_completed"
[(set (match_dup 0) (float_extend:HF (match_dup 2)))
(set (match_dup 0) (unspec:HF [(subreg:QI (match_dup 0) 0)
(match_dup 3)] UNSPEC_LOADHF_INT))]
"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:HF [(subreg:QI (match_dup 0) 0)
(match_dup 3)] UNSPEC_LOADHF_INT))]
"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:QI [(match_dup 1)] UNSPEC_STOREHF_INT))]
"operands[2] = c4x_operand_subword (operands[0], 0, 1, HFmode);
operands[3] = c4x_operand_subword (operands[0], 1, 1, HFmode);
PUT_MODE (operands[2], QFmode);
PUT_MODE (operands[3], QImode);")
 
(define_insn "*loadhf_float"
[(set (match_operand:HF 0 "reg_operand" "=h")
(float_extend:HF (match_operand:QF 1 "src_operand" "fHm")))]
""
"ldfu\\t%1,%0"
[(set_attr "type" "unary")])
 
(define_insn "*loadhf_int"
[(set (match_operand:HF 0 "reg_operand" "+h")
(unspec:HF [(subreg:QI (match_dup 0) 0)
(match_operand:QI 1 "src_operand" "rIm")] UNSPEC_LOADHF_INT))]
""
"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:QI [(match_operand:HF 1 "reg_operand" "h")] UNSPEC_STOREHF_INT))]
""
"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:QI [(match_dup 0)] UNSPEC_STOREHF_INT))]
"")
 
(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:QI [(match_operand:HF 0 "reg_operand" "h")] UNSPEC_STOREHF_INT))]
""
"push\\t%0"
[(set_attr "type" "push")])
 
; we cannot 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:HF [(subreg:QI (match_dup 0) 0)
(mem:QI (post_dec:QI (reg:QI 20)))] UNSPEC_LOADHF_INT))
(clobber (reg:CC 21))])]
"")
 
(define_insn "*pophf_int"
[(set (match_operand:HF 0 "reg_operand" "+h")
(unspec:HF [(subreg:QI (match_dup 0) 0)
(mem:QI (post_dec:QI (reg:QI 20)))] UNSPEC_LOADHF_INT))
(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" "pop")])
 
;
; FIX
;
(define_expand "fixuns_trunchfqi2"
[(parallel [(set (match_dup 2)
(fix:QI (match_operand:HF 1 "reg_or_const_operand" "hH")))
(clobber (reg:CC 21))])
(parallel [(set (match_dup 3)
(minus:HF (match_dup 1) (match_dup 5)))
(clobber (reg:CC_NOOV 21))])
(parallel [(set (reg:CC 21)
(compare:CC (fix:QI (match_dup 3))
(const_int 0)))
(set (match_dup 4)
(fix:QI (match_dup 3)))])
(parallel [(set (match_dup 4) (unspec:QI [(match_dup 2)] UNSPEC_LDIV))
(use (reg:CC 21))])
(set (match_operand:QI 0 "reg_operand" "=r") (match_dup 4))]
""
"operands[2] = gen_reg_rtx (QImode);
operands[3] = gen_reg_rtx (HFmode);
operands[4] = gen_reg_rtx (QImode);
operands[5] = gen_reg_rtx (HFmode);
emit_move_insn (operands[5], CONST_DOUBLE_ATOF (\"4294967296.0\", HFmode));")
 
(define_expand "fix_trunchfqi2"
[(parallel [(set (match_dup 2)
(fix:QI (match_operand:HF 1 "reg_or_const_operand" "")))
(clobber (reg:CC 21))])
(parallel [(set (match_dup 3) (neg:HF (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_fixhfqi_clobber (operands[0], operands[1]));
DONE;
}
operands[2] = gen_reg_rtx (QImode);
operands[3] = gen_reg_rtx (HFmode);
operands[4] = gen_reg_rtx (QImode);
operands[5] = gen_reg_rtx (QImode);
")
 
(define_insn "*fixhfqi_set"
[(set (reg:CC 21)
(compare:CC (fix:QI (match_operand:HF 1 "reg_or_const_operand" "hH"))
(const_int 0)))
(set (match_operand:QI 0 "ext_reg_operand" "=d")
(fix:QI (match_dup 1)))]
""
"fix\\t%1,%0"
[(set_attr "type" "unarycc")])
 
(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")])
 
(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_libfunc, 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_libfunc, UNSIGNED_FIX,
HImode, HFmode, 2, operands);
DONE;")
 
;
; 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_NOOV 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_NOOV 21))]
""
"negf\\t%1,%0"
[(set_attr "type" "unarycc")])
 
(define_insn "*neghf2_test"
[(set (reg:CC_NOOV 21)
(compare:CC_NOOV (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_NOOV 21)
(compare:CC_NOOV (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:HF [(match_operand:HF 1 "reg_or_const_operand" "hH")] UNSPEC_RCPF))
(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:HF [(match_operand:HF 1 "reg_or_const_operand" "hH")] UNSPEC_RSQRF))
(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:HF [(match_operand:HF 1 "reg_or_const_operand" "hH")] UNSPEC_RND))
(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:HF [(match_operand:HF 1 "reg_operand" "")] UNSPEC_RSQRF))
(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] = CONST_DOUBLE_ATOF (\"0.5\", HFmode);
operands[6] = CONST_DOUBLE_ATOF (\"1.5\", HFmode);
")
 
 
(define_expand "sqrthf2"
[(parallel [(set (match_operand:HF 0 "reg_operand" "")
(sqrt:HF (match_operand:HF 1 "reg_operand" "")))
(clobber (reg:CC 21))])]
"! TARGET_C3X && TARGET_INLINE"
"emit_insn (gen_sqrthf2_inline (operands[0], operands[1]));
DONE;")
 
;
; THREE OPERAND LONG DOUBLE INSTRUCTIONS
;
 
;
; ADDF
;
(define_insn "addhf3"
[(set (match_operand:HF 0 "reg_operand" "=h,?h")
(plus:HF (match_operand:HF 1 "reg_operand" "%0,h")
(match_operand:HF 2 "reg_or_const_operand" "H,h")))
(clobber (reg:CC_NOOV 21))]
""
"@
addf\\t%2,%0
addf3\\t%2,%1,%0"
[(set_attr "type" "binarycc,binarycc")])
 
;
; SUBF
;
(define_insn "subhf3"
[(set (match_operand:HF 0 "reg_operand" "=h,h,?h")
(minus:HF (match_operand:HF 1 "reg_or_const_operand" "0,H,h")
(match_operand:HF 2 "reg_or_const_operand" "H,0,h")))
(clobber (reg:CC_NOOV 21))]
""
"@
subf\\t%2,%0
subrf\\t%1,%0
subf3\\t%2,%1,%0"
[(set_attr "type" "binarycc,binarycc,binarycc")])
 
;
; MULF
;
; The C3x MPYF only uses 24-bit precision while the C4x uses 32-bit precision.
;
(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))])]
"! TARGET_C3X"
"")
 
(define_insn "*mulhf3_c40"
[(set (match_operand:HF 0 "reg_operand" "=h,?h")
(mult:HF (match_operand:HF 1 "reg_operand" "%0,h")
(match_operand:HF 2 "reg_or_const_operand" "hH,h")))
(clobber (reg:CC_NOOV 21))]
""
"@
mpyf\\t%2,%0
mpyf3\\t%2,%1,%0"
[(set_attr "type" "binarycc,binarycc")])
 
;
; CMPF
;
(define_expand "cmphf"
[(set (reg:CC 21)
(compare:CC (match_operand:HF 0 "reg_operand" "")
(match_operand:HF 1 "reg_or_const_operand" "")))]
""
"c4x_compare_op0 = operands[0];
c4x_compare_op1 = operands[1];
DONE;")
 
(define_insn "*cmphf"
[(set (reg:CC 21)
(compare:CC (match_operand:HF 0 "reg_operand" "h")
(match_operand:HF 1 "reg_or_const_operand" "hH")))]
""
"cmpf\\t%1,%0"
[(set_attr "type" "compare")])
 
(define_insn "*cmphf_noov"
[(set (reg:CC_NOOV 21)
(compare:CC_NOOV (match_operand:HF 0 "reg_operand" "h")
(match_operand:HF 1 "reg_or_const_operand" "hH")))]
""
"cmpf\\t%1,%0"
[(set_attr "type" "compare")])
 
; Inlined float divide for C4x
(define_expand "divhf3_inline"
[(parallel [(set (match_dup 3)
(unspec:HF [(match_operand:HF 2 "reg_operand" "")] UNSPEC_RCPF))
(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))])]
"! TARGET_C3X && TARGET_INLINE"
"emit_insn (gen_divhf3_inline (operands[0], operands[1], operands[2]));
DONE;")
 
 
;
; TWO OPERAND LONG LONG INSTRUCTIONS
;
 
(define_insn "*movhi_stik"
[(set (match_operand:HI 0 "memory_operand" "=m")
(match_operand:HI 1 "stik_const_operand" "K"))]
"! TARGET_C3X"
"#"
[(set_attr "type" "multi")])
 
; We could load some constants using define_splits for the C30
; in the large memory model---these would emit shift and or insns.
(define_expand "movhi"
[(set (match_operand:HI 0 "src_operand" "")
(match_operand:HI 1 "src_operand" ""))]
""
"if (c4x_emit_move_sequence (operands, HImode))
DONE;")
 
; The constraints for movhi must include 'r' if we don't
; restrict HImode regnos to start on an even number, since
; we can get RC, R8 allocated as a pair. We want more
; votes for FP_REGS so we use dr as the constraints.
(define_insn "*movhi_noclobber"
[(set (match_operand:HI 0 "dst_operand" "=dr,m")
(match_operand:HI 1 "src_operand" "drIm,r"))]
"reg_operand (operands[0], HImode)
|| reg_operand (operands[1], HImode)"
"#"
[(set_attr "type" "multi,multi")])
 
; This will fail miserably if the destination register is used in the
; source memory address.
; The usual strategy in this case is to swap the order of insns we emit,
; however, this will fail if we have an autoincrement memory address.
; For example:
; ldi *ar0++, ar0
; ldi *ar0++, ar1
;
; We could convert this to
; ldi *ar0(1), ar1
; ldi *ar0, ar0
;
; However, things are likely to be very screwed up if we get this.
 
(define_split
[(set (match_operand:HI 0 "dst_operand" "")
(match_operand:HI 1 "src_operand" ""))]
"reload_completed
&& (reg_operand (operands[0], HImode)
|| reg_operand (operands[1], HImode)
|| stik_const_operand (operands[1], HImode))"
[(set (match_dup 2) (match_dup 4))
(set (match_dup 3) (match_dup 5))]
"operands[2] = c4x_operand_subword (operands[0], 0, 1, HImode);
operands[3] = c4x_operand_subword (operands[0], 1, 1, HImode);
operands[4] = c4x_operand_subword (operands[1], 0, 1, HImode);
operands[5] = c4x_operand_subword (operands[1], 1, 1, HImode);
if (reg_overlap_mentioned_p (operands[2], operands[5]))
{
/* Swap order of move insns. */
rtx tmp;
tmp = operands[2];
operands[2] =operands[3];
operands[3] = tmp;
tmp = operands[4];
operands[4] =operands[5];
operands[5] = tmp;
}")
 
 
(define_insn "extendqihi2"
[(set (match_operand:HI 0 "reg_operand" "=dc")
(sign_extend:HI (match_operand:QI 1 "src_operand" "rIm")))
(clobber (reg:CC 21))]
""
"#"
[(set_attr "type" "multi")])
 
(define_split
[(set (match_operand:HI 0 "reg_operand" "")
(sign_extend:HI (match_operand:QI 1 "src_operand" "")))
(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" "")
(sign_extend:HI (match_operand:QI 1 "src_operand" "")))
(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 "nonimmediate_src_operand" "rm")))
(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" "")
(zero_extend:HI (match_operand:QI 1 "nonimmediate_src_operand" "")))
(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_libfunc, 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_libfunc, 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_libfunc, 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_libfunc, UNSIGNED_FLOAT,
HFmode, HImode, 2, operands);
DONE;")
 
 
;
; THREE OPERAND LONG LONG INSTRUCTIONS
;
 
(define_expand "addhi3"
[(parallel [(set (match_operand:HI 0 "ext_reg_operand" "")
(plus:HI (match_operand:HI 1 "src_operand" "")
(match_operand:HI 2 "src_operand" "")))
(clobber (reg:CC_NOOV 21))])]
""
"legitimize_operands (PLUS, operands, HImode);")
 
(define_insn "*addhi3_clobber"
[(set (match_operand:HI 0 "ext_reg_operand" "=d,d,?d")
(plus:HI (match_operand:HI 1 "src_operand" "%0,rR,rS<>")
(match_operand:HI 2 "src_operand" "rm,R,rS<>")))
(clobber (reg:CC_NOOV 21))]
"valid_operands (PLUS, operands, HImode)"
"#"
[(set_attr "type" "multi,multi,multi")])
 
(define_split
[(set (match_operand:HI 0 "ext_reg_operand" "")
(plus:HI (match_operand:HI 1 "src_operand" "")
(match_operand:HI 2 "src_operand" "")))
(clobber (reg:CC_NOOV 21))]
"reload_completed"
[(parallel [(set (reg:CC_NOOV 21)
(compare:CC_NOOV (plus:QI (match_dup 4) (match_dup 5))
(const_int 0)))
(set (match_dup 3) (plus:QI (match_dup 4) (match_dup 5)))])
(parallel [(set (match_dup 6) (plus:QI (match_dup 7) (match_dup 8)))
(use (reg:CC_NOOV 21))
(clobber (reg:CC_NOOV 21))])]
"operands[3] = c4x_operand_subword (operands[0], 0, 1, HImode);
operands[4] = c4x_operand_subword (operands[1], 0, 1, HImode);
operands[5] = c4x_operand_subword (operands[2], 0, 1, HImode);
operands[6] = c4x_operand_subword (operands[0], 1, 1, HImode);
operands[7] = c4x_operand_subword (operands[1], 1, 1, HImode);
operands[8] = c4x_operand_subword (operands[2], 1, 1, HImode);")
 
(define_expand "subhi3"
[(parallel [(set (match_operand:HI 0 "ext_reg_operand" "")
(minus:HI (match_operand:HI 1 "src_operand" "")
(match_operand:HI 2 "src_operand" "")))
(clobber (reg:CC_NOOV 21))])]
""
"legitimize_operands (MINUS, operands, HImode);")
 
 
(define_insn "*subhi3_clobber"
[(set (match_operand:HI 0 "ext_reg_operand" "=d,d,?d")
(minus:HI (match_operand:HI 1 "src_operand" "0,rR,rS<>")
(match_operand:HI 2 "src_operand" "rm,R,rS<>")))
(clobber (reg:CC_NOOV 21))]
"valid_operands (MINUS, operands, HImode)"
"#"
[(set_attr "type" "multi,multi,multi")])
 
(define_split
[(set (match_operand:HI 0 "ext_reg_operand" "")
(minus:HI (match_operand:HI 1 "src_operand" "")
(match_operand:HI 2 "src_operand" "")))
(clobber (reg:CC_NOOV 21))]
"reload_completed"
[(parallel [(set (reg:CC_NOOV 21)
(compare:CC_NOOV (minus:QI (match_dup 4) (match_dup 5))
(const_int 0)))
(set (match_dup 3) (minus:QI (match_dup 4) (match_dup 5)))])
(parallel [(set (match_dup 6) (minus:QI (match_dup 7) (match_dup 8)))
(use (reg:CC_NOOV 21))
(clobber (reg:CC_NOOV 21))])]
"operands[3] = c4x_operand_subword (operands[0], 0, 1, HImode);
operands[4] = c4x_operand_subword (operands[1], 0, 1, HImode);
operands[5] = c4x_operand_subword (operands[2], 0, 1, HImode);
operands[6] = c4x_operand_subword (operands[0], 1, 1, HImode);
operands[7] = c4x_operand_subword (operands[1], 1, 1, HImode);
operands[8] = c4x_operand_subword (operands[2], 1, 1, HImode);")
 
(define_expand "iorhi3"
[(parallel [(set (match_operand:HI 0 "reg_operand" "")
(ior:HI (match_operand:HI 1 "src_operand" "")
(match_operand:HI 2 "src_operand" "")))
(clobber (reg:CC 21))])]
""
"legitimize_operands (IOR, operands, HImode);")
 
(define_insn "*iorhi3_clobber"
[(set (match_operand:HI 0 "reg_operand" "=d,d,?d")
(ior:HI (match_operand:HI 1 "src_operand" "%0,rR,rS<>")
(match_operand:HI 2 "src_operand" "rm,R,rS<>")))
(clobber (reg:CC 21))]
"valid_operands (IOR, operands, HImode)"
"#"
[(set_attr "type" "multi,multi,multi")])
 
(define_split
[(set (match_operand:HI 0 "reg_operand" "")
(ior:HI (match_operand:HI 1 "src_operand" "")
(match_operand:HI 2 "src_operand" "")))
(clobber (reg:CC 21))]
"reload_completed"
[(parallel [(set (match_dup 3) (ior:QI (match_dup 4) (match_dup 5)))
(clobber (reg:CC 21))])
(parallel [(set (match_dup 6) (ior:QI (match_dup 7) (match_dup 8)))
(clobber (reg:CC 21))])]
"operands[3] = c4x_operand_subword (operands[0], 0, 1, HImode);
operands[4] = c4x_operand_subword (operands[1], 0, 1, HImode);
operands[5] = c4x_operand_subword (operands[2], 0, 1, HImode);
operands[6] = c4x_operand_subword (operands[0], 1, 1, HImode);
operands[7] = c4x_operand_subword (operands[1], 1, 1, HImode);
operands[8] = c4x_operand_subword (operands[2], 1, 1, HImode);")
 
(define_expand "andhi3"
[(parallel [(set (match_operand:HI 0 "reg_operand" "")
(and:HI (match_operand:HI 1 "src_operand" "")
(match_operand:HI 2 "src_operand" "")))
(clobber (reg:CC 21))])]
""
"legitimize_operands (AND, operands, HImode);")
 
(define_insn "*andhi3_clobber"
[(set (match_operand:HI 0 "reg_operand" "=d,d,?d")
(and:HI (match_operand:HI 1 "src_operand" "%0,rR,rS<>")
(match_operand:HI 2 "src_operand" "rm,R,rS<>")))
(clobber (reg:CC 21))]
"valid_operands (AND, operands, HImode)"
"#"
[(set_attr "type" "multi,multi,multi")])
 
(define_split
[(set (match_operand:HI 0 "reg_operand" "")
(and:HI (match_operand:HI 1 "src_operand" "")
(match_operand:HI 2 "src_operand" "")))
(clobber (reg:CC 21))]
"reload_completed"
[(parallel [(set (match_dup 3) (and:QI (match_dup 4) (match_dup 5)))
(clobber (reg:CC 21))])
(parallel [(set (match_dup 6) (and:QI (match_dup 7) (match_dup 8)))
(clobber (reg:CC 21))])]
"operands[3] = c4x_operand_subword (operands[0], 0, 1, HImode);
operands[4] = c4x_operand_subword (operands[1], 0, 1, HImode);
operands[5] = c4x_operand_subword (operands[2], 0, 1, HImode);
operands[6] = c4x_operand_subword (operands[0], 1, 1, HImode);
operands[7] = c4x_operand_subword (operands[1], 1, 1, HImode);
operands[8] = c4x_operand_subword (operands[2], 1, 1, HImode);")
 
(define_expand "xorhi3"
[(parallel [(set (match_operand:HI 0 "reg_operand" "")
(xor:HI (match_operand:HI 1 "src_operand" "")
(match_operand:HI 2 "src_operand" "")))
(clobber (reg:CC 21))])]
""
"legitimize_operands (XOR, operands, HImode);")
 
 
(define_insn "*xorhi3_clobber"
[(set (match_operand:HI 0 "reg_operand" "=d,d,?d")
(xor:HI (match_operand:HI 1 "src_operand" "%0,rR,rS<>")
(match_operand:HI 2 "src_operand" "rm,R,rS<>")))
(clobber (reg:CC 21))]
"valid_operands (XOR, operands, HImode)"
"#"
[(set_attr "type" "multi,multi,multi")])
 
(define_split
[(set (match_operand:HI 0 "reg_operand" "")
(xor:HI (match_operand:HI 1 "src_operand" "")
(match_operand:HI 2 "src_operand" "")))
(clobber (reg:CC 21))]
"reload_completed"
[(parallel [(set (match_dup 3) (xor:QI (match_dup 4) (match_dup 5)))
(clobber (reg:CC 21))])
(parallel [(set (match_dup 6) (xor:QI (match_dup 7) (match_dup 8)))
(clobber (reg:CC 21))])]
"operands[3] = c4x_operand_subword (operands[0], 0, 1, HImode);
operands[4] = c4x_operand_subword (operands[1], 0, 1, HImode);
operands[5] = c4x_operand_subword (operands[2], 0, 1, HImode);
operands[6] = c4x_operand_subword (operands[0], 1, 1, HImode);
operands[7] = c4x_operand_subword (operands[1], 1, 1, HImode);
operands[8] = c4x_operand_subword (operands[2], 1, 1, HImode);")
 
(define_expand "ashlhi3"
[(parallel [(set (match_operand:HI 0 "reg_operand" "")
(ashift:HI (match_operand:HI 1 "src_operand" "")
(match_operand:QI 2 "src_operand" "")))
(clobber (reg:CC 21))])]
""
"if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) >= 32)
{
rtx op0hi = operand_subword (operands[0], 1, 0, HImode);
rtx op0lo = operand_subword (operands[0], 0, 0, HImode);
rtx op1lo = operand_subword (operands[1], 0, 0, HImode);
rtx count = GEN_INT ((INTVAL (operands[2]) - 32));
 
if (INTVAL (count))
emit_insn (gen_ashlqi3 (op0hi, op1lo, count));
else
emit_insn (gen_movqi (op0hi, op1lo));
emit_insn (gen_movqi (op0lo, const0_rtx));
DONE;
}
if (! REG_P (operands[1]))
operands[1] = force_reg (HImode, operands[1]);
emit_insn (gen_ashlhi3_reg (operands[0], operands[1], operands[2]));
DONE;
")
 
; %0.lo = %1.lo << %2
; %0.hi = (%1.hi << %2 ) | (%1.lo >> (32 - %2))
; This algorithm should work for shift counts greater than 32
(define_expand "ashlhi3_reg"
[(use (match_operand:HI 1 "reg_operand" ""))
(use (match_operand:HI 0 "reg_operand" ""))
/* If the shift count is greater than 32 this will give zero. */
(parallel [(set (match_dup 7)
(ashift:QI (match_dup 3)
(match_operand:QI 2 "reg_operand" "")))
(clobber (reg:CC 21))])
/* If the shift count is greater than 32 this will give zero. */
(parallel [(set (match_dup 8)
(ashift:QI (match_dup 4) (match_dup 2)))
(clobber (reg:CC 21))])
(parallel [(set (match_dup 10)
(plus:QI (match_dup 2) (const_int -32)))
(clobber (reg:CC_NOOV 21))])
/* If the shift count is greater than 32 this will do a left shift. */
(parallel [(set (match_dup 9)
(lshiftrt:QI (match_dup 3) (neg:QI (match_dup 10))))
(clobber (reg:CC 21))])
(set (match_dup 5) (match_dup 7))
(parallel [(set (match_dup 6)
(ior:QI (match_dup 8) (match_dup 9)))
(clobber (reg:CC 21))])]
""
"
operands[3] = operand_subword (operands[1], 0, 1, HImode); /* lo */
operands[4] = operand_subword (operands[1], 1, 1, HImode); /* hi */
operands[5] = operand_subword (operands[0], 0, 1, HImode); /* lo */
operands[6] = operand_subword (operands[0], 1, 1, HImode); /* hi */
operands[7] = gen_reg_rtx (QImode); /* lo << count */
operands[8] = gen_reg_rtx (QImode); /* hi << count */
operands[9] = gen_reg_rtx (QImode); /* lo >> (32 - count) */
operands[10] = gen_reg_rtx (QImode); /* 32 - count */
")
 
; This should do all the dirty work with define_split
(define_expand "lshrhi3"
[(parallel [(set (match_operand:HI 0 "reg_operand" "")
(lshiftrt:HI (match_operand:HI 1 "src_operand" "")
(match_operand:QI 2 "src_operand" "")))
(clobber (reg:CC 21))])]
""
"if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) >= 32)
{
rtx op0hi = operand_subword (operands[0], 1, 0, HImode);
rtx op0lo = operand_subword (operands[0], 0, 0, HImode);
rtx op1hi = operand_subword (operands[1], 1, 0, HImode);
rtx count = GEN_INT ((INTVAL (operands[2]) - 32));
 
if (INTVAL (count))
emit_insn (gen_lshrqi3 (op0lo, op1hi, count));
else
emit_insn (gen_movqi (op0lo, op1hi));
emit_insn (gen_movqi (op0hi, const0_rtx));
DONE;
}
if (! REG_P (operands[1]))
operands[1] = force_reg (HImode, operands[1]);
emit_insn (gen_lshrhi3_reg (operands[0], operands[1], operands[2]));
DONE;")
 
; %0.hi = %1.hi >> %2
; %0.lo = (%1.lo >> %2 ) | (%1.hi << (32 - %2))
; This algorithm should work for shift counts greater than 32
(define_expand "lshrhi3_reg"
[(use (match_operand:HI 1 "reg_operand" ""))
(use (match_operand:HI 0 "reg_operand" ""))
(parallel [(set (match_dup 11)
(neg:QI (match_operand:QI 2 "reg_operand" "")))
(clobber (reg:CC_NOOV 21))])
/* If the shift count is greater than 32 this will give zero. */
(parallel [(set (match_dup 7)
(lshiftrt:QI (match_dup 3)
(neg:QI (match_dup 11))))
(clobber (reg:CC 21))])
/* If the shift count is greater than 32 this will give zero. */
(parallel [(set (match_dup 8)
(lshiftrt:QI (match_dup 4)
(neg:QI (match_dup 11))))
(clobber (reg:CC 21))])
(parallel [(set (match_dup 10)
(plus:QI (match_dup 11) (const_int 32)))
(clobber (reg:CC_NOOV 21))])
/* If the shift count is greater than 32 this will do an arithmetic
right shift. However, we need a logical right shift. */
(parallel [(set (match_dup 9)
(ashift:QI (match_dup 4) (unspec:QI [(match_dup 10)] UNSPEC_LSH)))
(clobber (reg:CC 21))])
(set (match_dup 6) (match_dup 8))
(parallel [(set (match_dup 5)
(ior:QI (match_dup 7) (match_dup 9)))
(clobber (reg:CC 21))])]
""
"
operands[3] = operand_subword (operands[1], 0, 1, HImode); /* lo */
operands[4] = operand_subword (operands[1], 1, 1, HImode); /* hi */
operands[5] = operand_subword (operands[0], 0, 1, HImode); /* lo */
operands[6] = operand_subword (operands[0], 1, 1, HImode); /* hi */
operands[7] = gen_reg_rtx (QImode); /* lo >> count */
operands[8] = gen_reg_rtx (QImode); /* hi >> count */
operands[9] = gen_reg_rtx (QImode); /* hi << (32 - count) */
operands[10] = gen_reg_rtx (QImode); /* 32 - count */
operands[11] = gen_reg_rtx (QImode); /* -count */
")
 
; This should do all the dirty work with define_split
(define_expand "ashrhi3"
[(parallel [(set (match_operand:HI 0 "reg_operand" "")
(ashiftrt:HI (match_operand:HI 1 "src_operand" "")
(match_operand:QI 2 "src_operand" "")))
(clobber (reg:CC 21))])]
""
"if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) >= 32)
{
rtx op0hi = operand_subword (operands[0], 1, 0, HImode);
rtx op0lo = operand_subword (operands[0], 0, 0, HImode);
rtx op1hi = operand_subword (operands[1], 1, 0, HImode);
rtx count = GEN_INT ((INTVAL (operands[2]) - 32));
 
if (INTVAL (count))
emit_insn (gen_ashrqi3 (op0lo, op1hi, count));
else
emit_insn (gen_movqi (op0lo, op1hi));
emit_insn (gen_ashrqi3 (op0hi, op1hi, GEN_INT (31)));
DONE;
}
if (! REG_P (operands[1]))
operands[1] = force_reg (HImode, operands[1]);
emit_insn (gen_ashrhi3_reg (operands[0], operands[1], operands[2]));
DONE;")
 
; %0.hi = %1.hi >> %2
; %0.lo = (%1.lo >> %2 ) | (%1.hi << (32 - %2))
; This algorithm should work for shift counts greater than 32
(define_expand "ashrhi3_reg"
[(use (match_operand:HI 1 "reg_operand" ""))
(use (match_operand:HI 0 "reg_operand" ""))
(parallel [(set (match_dup 11)
(neg:QI (match_operand:QI 2 "reg_operand" "")))
(clobber (reg:CC_NOOV 21))])
/* If the shift count is greater than 32 this will give zero. */
(parallel [(set (match_dup 7)
(lshiftrt:QI (match_dup 3)
(neg:QI (match_dup 11))))
(clobber (reg:CC 21))])
/* If the shift count is greater than 32 this will give zero. */
(parallel [(set (match_dup 8)
(ashiftrt:QI (match_dup 4)
(neg:QI (match_dup 11))))
(clobber (reg:CC 21))])
(parallel [(set (match_dup 10)
(plus:QI (match_dup 11) (const_int 32)))
(clobber (reg:CC_NOOV 21))])
/* If the shift count is greater than 32 this will do an arithmetic
right shift. */
(parallel [(set (match_dup 9)
(ashift:QI (match_dup 4) (match_dup 10)))
(clobber (reg:CC 21))])
(set (match_dup 6) (match_dup 8))
(parallel [(set (match_dup 5)
(ior:QI (match_dup 7) (match_dup 9)))
(clobber (reg:CC 21))])]
""
"
operands[3] = operand_subword (operands[1], 0, 1, HImode); /* lo */
operands[4] = operand_subword (operands[1], 1, 1, HImode); /* hi */
operands[5] = operand_subword (operands[0], 0, 1, HImode); /* lo */
operands[6] = operand_subword (operands[0], 1, 1, HImode); /* hi */
operands[7] = gen_reg_rtx (QImode); /* lo >> count */
operands[8] = gen_reg_rtx (QImode); /* hi >> count */
operands[9] = gen_reg_rtx (QImode); /* hi << (32 - count) */
operands[10] = gen_reg_rtx (QImode); /* 32 - count */
operands[11] = gen_reg_rtx (QImode); /* -count */
")
 
(define_expand "cmphi"
[(set (reg:CC 21)
(compare:CC (match_operand:HI 0 "src_operand" "")
(match_operand:HI 1 "src_operand" "")))]
""
"legitimize_operands (COMPARE, operands, HImode);
c4x_compare_op0 = operands[0];
c4x_compare_op1 = operands[1];
DONE;")
 
(define_insn "*cmphi_cc"
[(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)"
"#"
[(set_attr "type" "multi")])
 
(define_insn "*cmphi_cc_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)"
"#"
[(set_attr "type" "multi")])
 
; 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:CC [(compare:CC (match_dup 0)
(match_dup 1))] UNSPEC_CMPHI))
(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:CC_NOOV [(compare:CC_NOOV (match_dup 0)
(match_dup 1))] UNSPEC_CMPHI))
(clobber (match_scratch:QI 2 ""))
(clobber (match_scratch:QI 3 ""))])]
"")
 
; This is normally not used. The define splits above are used first.
(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)
(compare:CC (match_dup 0) (match_dup 1)))
(use (reg:QI 20))])]
"")
 
(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)
(compare:CC_NOOV (match_dup 0) (match_dup 1)))
(use (reg:QI 20))])]
"")
 
(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<>")))
(use (reg:QI 20))]
"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<>")))
(use (reg:QI 20))]
"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:CC [(compare:CC (match_operand:HI 0 "src_operand" "rR,rS<>")
(match_operand:HI 1 "src_operand" "R,rS<>"))] UNSPEC_CMPHI))
(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:CC_NOOV [(compare:CC_NOOV (match_operand:HI 0 "src_operand" "rR,rS<>")
(match_operand:HI 1 "src_operand" "R,rS<>"))] UNSPEC_CMPHI))
(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 (smul_optab->handlers[(int) HImode].libfunc,
MULT, 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)))
(use (reg:QI 20))
(clobber (reg:CC_NOOV 21))])]
"! c4x_label_conflict (insn, operands[2], operands[1])"
"db%I3\\t%0,%l1\\n\\tb%3\\t%l2"
[(set_attr "type" "multi")])
 
(define_peephole
[(set (pc) (if_then_else (match_operator 3 "comparison_operator"
[(reg:CC 21) (const_int 0)])
(label_ref (match_operand 2 "" ""))
(pc)))
(parallel
[(set (pc)
(if_then_else
(ne (match_operand:QI 0 "addr_reg_operand" "+a")
(const_int 0))
(label_ref (match_operand 1 "" ""))
(pc)))
(set (match_dup 0)
(plus:QI (match_dup 0)
(const_int -1)))])]
"! c4x_label_conflict (insn, operands[2], operands[1])"
"db%I3\\t%0,%l1\\n\\tb%3\\t%l2"
[(set_attr "type" "multi")])
 
;
; Peepholes to convert 'call label; rets' into jump label
;
 
(define_peephole
[(parallel [(call (mem:QI (match_operand:QI 0 "call_address_operand" ""))
(match_operand:QI 1 "general_operand" ""))
(clobber (reg:QI 31))])
(return)]
"! c4x_null_epilogue_p ()"
"*
if (REG_P (operands[0]))
return \"bu%#\\t%C0\";
else
return \"br%#\\t%C0\";"
[(set_attr "type" "jump")])
 
(define_peephole
[(parallel [(set (match_operand 0 "" "")
(call (mem:QI (match_operand:QI 1 "call_address_operand" ""))
(match_operand:QI 2 "general_operand" "")))
(clobber (reg:QI 31))])
(return)]
"! c4x_null_epilogue_p ()"
"*
if (REG_P (operands[1]))
return \"bu%#\\t%C1\";
else
return \"br%#\\t%C1\";"
[(set_attr "type" "jump")])
 
 
; 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")
 
 
; 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++")
 
 
; The following two peepholes remove an unnecessary load
; often found at the end of a function. These peepholes
; could be generalized to other binary operators. They shouldn't
; be required if we run a post reload mop-up pass.
(define_peephole
[(parallel [(set (match_operand:QF 0 "ext_reg_operand" "")
(plus:QF (match_operand:QF 1 "ext_reg_operand" "")
(match_operand:QF 2 "ext_reg_operand" "")))
(clobber (reg:CC_NOOV 21))])
(set (match_operand:QF 3 "ext_reg_operand" "")
(match_dup 0))]
"dead_or_set_p (insn, operands[0])"
"addf3\\t%2,%1,%3")
 
(define_peephole
[(parallel [(set (match_operand:QI 0 "reg_operand" "")
(plus:QI (match_operand:QI 1 "reg_operand" "")
(match_operand:QI 2 "reg_operand" "")))
(clobber (reg:CC_NOOV 21))])
(set (match_operand:QI 3 "reg_operand" "")
(match_dup 0))]
"dead_or_set_p (insn, operands[0])"
"addi3\\t%2,%1,%3")

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.