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, §)) |
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, §)) |
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") |