#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;
enum reg_class c4x_regclass_map[FIRST_PSEUDO_REGISTER] =
{
R0R1_REGS,
R0R1_REGS,
R2R3_REGS,
R2R3_REGS,
EXT_LOW_REGS,
EXT_LOW_REGS,
EXT_LOW_REGS,
EXT_LOW_REGS,
ADDR_REGS,
ADDR_REGS,
ADDR_REGS,
ADDR_REGS,
ADDR_REGS,
ADDR_REGS,
ADDR_REGS,
ADDR_REGS,
DP_REG,
INDEX_REGS,
INDEX_REGS,
BK_REG,
SP_REG,
ST_REG,
NO_REGS,
NO_REGS,
NO_REGS,
INT_REGS,
INT_REGS,
RC_REG,
EXT_REGS,
EXT_REGS,
EXT_REGS,
EXT_REGS,
};
enum machine_mode c4x_caller_save_map[FIRST_PSEUDO_REGISTER] =
{
HFmode,
HFmode,
HFmode,
HFmode,
QFmode,
QFmode,
QImode,
QImode,
QImode,
QImode,
QImode,
QImode,
QImode,
QImode,
QImode,
QImode,
VOIDmode,
QImode,
QImode,
QImode,
VOIDmode,
VOIDmode,
VOIDmode,
VOIDmode,
VOIDmode,
QImode,
QImode,
VOIDmode,
QFmode,
HFmode,
HFmode,
HFmode,
};
rtx c4x_compare_op0;
rtx c4x_compare_op1;
const char *c4x_rpts_cycles_string;
int c4x_rpts_cycles = 0;
const char *c4x_cpu_version_string;
int c4x_cpu_version = 40;
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;
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_float_p (rtx);
static int c4x_a_register (rtx);
static int c4x_x_register (rtx);
static int c4x_immed_int_constant (rtx);
static int c4x_immed_float_constant (rtx);
static int c4x_K_constant (rtx);
static int c4x_N_constant (rtx);
static int c4x_O_constant (rtx);
static int c4x_R_indirect (rtx);
static int c4x_S_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 *);
#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
#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;
void
c4x_override_options (void)
{
if (c4x_rpts_cycles_string)
c4x_rpts_cycles = atoi (c4x_rpts_cycles_string);
else
c4x_rpts_cycles = 0;
if (TARGET_C30)
c4x_cpu_version = 30;
else if (TARGET_C31)
c4x_cpu_version = 31;
else if (TARGET_C32)
c4x_cpu_version = 32;
else if (TARGET_C33)
c4x_cpu_version = 33;
else if (TARGET_C40)
c4x_cpu_version = 40;
else if (TARGET_C44)
c4x_cpu_version = 44;
else
c4x_cpu_version = 40;
if (c4x_cpu_version_string)
{
const char *p = c4x_cpu_version_string;
if (*p == 'c' || *p == 'C')
p++;
c4x_cpu_version = atoi (p);
}
target_flags &= ~(C30_FLAG | C31_FLAG | C32_FLAG | C33_FLAG |
C40_FLAG | C44_FLAG);
switch (c4x_cpu_version)
{
case 30: target_flags |= C30_FLAG; break;
case 31: target_flags |= C31_FLAG; break;
case 32: target_flags |= C32_FLAG; break;
case 33: target_flags |= C33_FLAG; break;
case 40: target_flags |= C40_FLAG; break;
case 44: target_flags |= C44_FLAG; break;
default:
warning ("unknown CPU version %d, using 40.\n", c4x_cpu_version);
c4x_cpu_version = 40;
target_flags |= C40_FLAG;
}
if (TARGET_C30 || TARGET_C31 || TARGET_C32 || TARGET_C33)
target_flags |= C3X_FLAG;
else
target_flags &= ~C3X_FLAG;
set_fast_math_flags (1);
if (! TARGET_ALIASES && ! flag_argument_noalias)
flag_argument_noalias = 1;
}
void
c4x_optimization_options (int level ATTRIBUTE_UNUSED,
int size ATTRIBUTE_UNUSED)
{
flag_schedule_insns = 0;
}
#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;
special = *ptr == '\"' || *ptr == '\\';
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:
#endif
case QImode:
return IS_INT_REGNO (regno);
case QFmode:
case HFmode:
return IS_EXT_REGNO (regno);
case CCmode:
case CC_NOOVmode:
return IS_ST_REGNO (regno);
case HImode:
return IS_INT_REGNO (regno)
&& IS_INT_REGNO (regno + 1)
&& (regno & 1) == 0;
default:
return 0;
}
return 0;
}
int
c4x_hard_regno_rename_ok (unsigned int regno1, unsigned int regno2)
{
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;
if (IS_EXT_REGNO (regno1) && ! IS_EXT_REGNO (regno2))
return 0;
if (IS_EXT_REGNO (regno2) && ! IS_EXT_REGNO (regno1))
return 0;
return 1;
}
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};
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 (! next_param)
cum->var = 1;
if ((mode = TYPE_MODE (type)))
{
if (! targetm.calls.must_pass_in_stack (mode, type))
{
if (mode == QFmode || mode == HFmode)
cum->floats++;
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);
}
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))
{
if (mode == QFmode || mode == HFmode)
cum->floats++;
else if (mode == QImode || mode == Pmode)
cum->ints++;
}
else if (! TARGET_MEMPARM && ! type)
{
if (mode == QFmode || mode == HFmode)
cum->floats++;
else if (mode == QImode || mode == Pmode)
cum->ints++;
}
return;
}
struct rtx_def *
c4x_function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode,
tree type, int named)
{
int reg = 0;
if (! cum->init)
{
cum->maxfloats = (cum->floats > 2) ? 2 : cum->floats;
cum->maxints = (cum->ints > 6 - cum->maxfloats) ?
6 - cum->maxfloats : cum->ints;
if (! cum->prototype)
cum->maxints = 6;
cum->ints = cum->floats = 0;
cum->init = 1;
}
if (type == void_type_node)
return 0;
if (! TARGET_MEMPARM
&& named
&& type
&& ! targetm.calls.must_pass_in_stack (mode, type))
{
if (mode == QFmode || mode == HFmode)
{
if (cum->floats < cum->maxfloats)
reg = c4x_fp_reglist[cum->floats];
}
else if (mode == QImode || mode == Pmode)
{
if (cum->ints < cum->maxints)
reg = c4x_int_reglist[cum->maxfloats][cum->ints];
}
}
else if (! TARGET_MEMPARM && ! type)
{
if (mode == QFmode || mode == HFmode)
reg = c4x_fp_reglist[cum->floats];
else if (mode == QImode || mode == Pmode)
reg = c4x_int_reglist[0][cum->ints];
}
if (TARGET_DEBUG)
{
fprintf (stderr, "c4x_function_arg(mode=%s, named=%d",
GET_MODE_NAME (mode), named);
if (reg)
fprintf (stderr, ", reg=%s", reg_names[reg]);
else
fprintf (stderr, ", stack");
fprintf (stderr, ")\n");
}
if (reg)
return gen_rtx_REG (mode, reg);
else
return NULL_RTX;
}
static 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 = build (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_fold_indirect_ref (t);
if (indirect)
t = build_fold_indirect_ref (t);
return t;
}
static int
c4x_isr_reg_used_p (unsigned int regno)
{
if (regno == FRAME_POINTER_REGNUM
|| IS_ST_REGNO (regno))
return 0;
if (IS_DP_REGNO (regno))
return ! TARGET_SMALL || TARGET_PARANOID;
if (c4x_leaf_function)
return regs_ever_live[regno] && fixed_regs[regno] == 0;
return IS_EXT_REGNO (regno)
|| ((regs_ever_live[regno] || call_used_regs[regno])
&& fixed_regs[regno] == 0);
}
static int
c4x_leaf_function_p (void)
{
if (lookup_attribute ("leaf_pretend",
TYPE_ATTRIBUTES (TREE_TYPE (current_function_decl))))
return 1;
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;
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;
int dont_push_ar3;
if (c4x_naked_function_p ())
{
return;
}
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;
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;
}
}
}
}
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;
}
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
{
dont_push_ar3 = 1;
}
}
else
{
dont_push_ar3 = 0;
if ((size != 0) || (current_function_args_size != 0))
{
size += 1;
}
}
if (size > 32767)
{
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)
{
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 ();
if (c4x_naked_function_p ())
{
insn = emit_jump_insn (gen_return_from_epilogue ());
RTX_FRAME_RELATED_P (insn) = 1;
return;
}
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
{
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;
size += 2;
jump = 1;
dont_pop_ar3 = 1;
}
else
{
dont_pop_ar3 = 1;
}
}
else
{
dont_pop_ar3 = 0;
if (size || current_function_args_size)
{
size += 1;
}
}
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))
{
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)
{
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)
{
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))
{
op1 = XEXP (op1, 1);
}
if (symbolic_address_operand (op1, mode))
{
if (TARGET_LOAD_ADDRESS)
{
emit_insn (gen_load_immed_address (op0, op1));
return 1;
}
else
{
op1 = force_const_mem (Pmode, op1);
}
}
else if (mode == HFmode && CONSTANT_P (op1) && ! LEGITIMATE_CONSTANT_P (op1))
{
op1 = force_const_mem (mode, op1);
}
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))
{
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))
{
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;
}
operands[0] = op0;
operands[1] = op1;
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;
rtx indx = NULL_RTX;
rtx disp = NULL_RTX;
enum rtx_code code;
code = GET_CODE (addr);
switch (code)
{
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;
case REG:
base = addr;
break;
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;
indx = op1;
if (IS_INDEX_REG (base) || IS_ADDR_REG (indx))
{
base = op1;
indx = op0;
}
}
else
{
base = op0;
disp = op1;
}
break;
default:
return 0;
}
}
break;
case LO_SUM:
{
rtx op0 = XEXP (addr, 0);
rtx op1 = XEXP (addr, 1);
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;
case CONST:
case LABEL_REF:
case SYMBOL_REF:
if (! TARGET_EXPOSE_LDP && ! strict && mode != HFmode && mode != HImode)
return 1;
return 0;
case CONST_INT:
return 0;
case MEM:
return 0;
case CONST_DOUBLE:
fatal_insn ("using CONST_DOUBLE for address", addr);
default:
return 0;
}
if (base)
{
if (indx && (mode == HImode || mode == HFmode))
return 0;
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;
}
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;
}
if (disp)
{
if (GET_CODE (disp) != CONST_INT)
return 0;
if (mode == HImode || mode == HFmode)
{
if (! IS_DISP8_OFF_CONST (INTVAL (disp)))
return 0;
}
else
{
if (! IS_DISP8_CONST (INTVAL (disp)))
return 0;
}
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)
{
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;
}
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;
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);
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:
return 2;
case CONST_INT:
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 '#':
if (final_sequence)
fprintf (file, "d");
return;
}
code = GET_CODE (op);
switch (letter)
{
case 'A':
if (code == CONST_INT || code == SYMBOL_REF || code == CONST)
fprintf (file, "@");
break;
case 'H':
output_addr_const (file, op);
return;
case 'I':
code = reverse_condition (code);
break;
case 'L':
if (code != CONST_INT)
fatal_insn ("c4x_print_operand: %%L inconsistency", op);
fprintf (file, "%d", exact_log2 (INTVAL (op)));
return;
case 'N':
if (code != CONST_INT)
fatal_insn ("c4x_print_operand: %%N inconsistency", op);
fprintf (file, HOST_WIDE_INT_PRINT_DEC, ~INTVAL (op));
return;
case 'K':
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':
if (! TARGET_SMALL
&& 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':
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':
break;
case 'U':
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:
{
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)]);
}
else
{
fprintf (file, "*+%s(%s)",
reg_names[REGNO (op0)],
reg_names[REGNO (op1)]);
}
}
else if (INTVAL (op1) < 0)
{
fprintf (file, "*-%s(" HOST_WIDE_INT_PRINT_DEC ")",
reg_names[REGNO (op0)],
-INTVAL (op1));
}
else
{
fprintf (file, "*+%s(" HOST_WIDE_INT_PRINT_DEC ")",
reg_names[REGNO (op0)],
INTVAL (op1));
}
}
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;
case CONST_INT:
default:
fatal_insn ("c4x_print_operand_address: Bad operand case", addr);
break;
}
}
static 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;
}
exponent = (((convval[0] >> 24) & 0xff) ^ 0x80) - 0x80;
if (exponent == -128)
return 1;
if ((convval[0] & 0x00000fff) != 0 || convval[1] != 0)
return 0;
return (exponent <= 7)
&& (exponent >= -7);
}
int
c4x_rptb_nop_p (rtx insn)
{
rtx start_label;
int i;
start_label = XEXP (XEXP (SET_SRC (XVECEXP (PATTERN (insn), 0, 0)), 1), 0);
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++)
{
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 (GET_CODE (insn) == JUMP_INSN)
return 1;
insn = previous_insn (insn);
}
return 0;
}
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;
for (; insn; insn = PREV_INSN (insn))
if (insn == start_label)
break;
if (! insn)
return 0;
start = insn;
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;
}
}
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;
}
}
return 1;
}
void
c4x_rptb_insert (rtx insn)
{
rtx end_label;
rtx start_label;
rtx new_start_label;
rtx count_reg;
count_reg = XEXP (XEXP (SET_SRC (XVECEXP (PATTERN (insn), 0, 0)), 0), 0);
if (REGNO (count_reg) != RC_REGNO)
return;
start_label = XEXP (XEXP (SET_SRC (XVECEXP (PATTERN (insn), 0, 0)), 1), 0);
if (! c4x_rptb_valid_p (insn, start_label))
{
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);
}
static void
c4x_reorg (void)
{
rtx insn;
for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
{
if (INSN_P (insn))
{
int insn_code_number;
rtx old;
insn_code_number = recog_memoized (insn);
if (insn_code_number < 0)
continue;
if (insn_code_number == CODE_FOR_rptb_end)
c4x_rptb_insert(insn);
old = insn;
if (! INSN_DELETED_P (old))
insn = try_split (PATTERN(old), old, 1);
if (INSN_DELETED_P (old))
{
PUT_CODE (old, NOTE);
NOTE_LINE_NUMBER (old) = NOTE_INSN_DELETED;
NOTE_SOURCE_FILE (old) = 0;
}
}
}
}
static int
c4x_a_register (rtx op)
{
return REG_P (op) && IS_ADDR_OR_PSEUDO_REG (op);
}
static 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;
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));
}
static 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));
}
static int
c4x_N_constant (rtx op)
{
return c4x_immed_int_constant (op) && IS_NOT_UINT16_CONST (INTVAL (op));
}
static int
c4x_O_constant (rtx op)
{
return c4x_immed_int_constant (op) && IS_HIGH_CONST (INTVAL (op));
}
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;
if (mode == HImode || mode == HFmode)
return IS_DISP8_OFF_CONST (INTVAL (op1));
return IS_DISP8_CONST (INTVAL (op1));
}
break;
default:
break;
}
return 0;
}
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;
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);
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;
}
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);
}
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;
if (mode == HImode || mode == HFmode)
return IS_DISP1_OFF_CONST (INTVAL (op1));
return IS_DISP1_CONST (INTVAL (op1));
}
break;
default:
break;
}
return 0;
}
static int
c4x_S_indirect (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);
}
case PLUS:
{
rtx op0 = XEXP (op, 0);
rtx op1 = XEXP (op, 1);
if (REG_P (op0))
{
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;
}
int
c4x_T_constraint (rtx op)
{
if (GET_CODE (op) != MEM)
return 0;
op = XEXP (op, 0);
if (GET_CODE (op) != LO_SUM)
{
return GET_CODE (op) == SYMBOL_REF
&& GET_MODE (op) == Pmode
&& SYMBOL_REF_FUNCTION_P (op);
}
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;
}
int
c4x_U_constraint (rtx op)
{
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
any_operand (register rtx op ATTRIBUTE_UNUSED,
enum machine_mode mode ATTRIBUTE_UNUSED)
{
return 1;
}
int
fp_zero_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
{
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);
}
int
const_operand (register rtx op, register enum machine_mode mode)
{
switch (mode)
{
case QFmode:
case HFmode:
if (GET_CODE (op) != CONST_DOUBLE
|| GET_MODE (op) != mode
|| GET_MODE_CLASS (mode) != MODE_FLOAT)
return 0;
return c4x_immed_float_p (op);
#if Pmode != QImode
case Pmode:
#endif
case QImode:
if (GET_CODE (op) != CONST_INT
|| (GET_MODE (op) != VOIDmode && GET_MODE (op) != mode)
|| GET_MODE_CLASS (mode) != MODE_INT)
return 0;
return IS_HIGH_CONST (INTVAL (op)) || IS_INT16_CONST (INTVAL (op));
case HImode:
return 0;
default:
return 0;
}
}
int
stik_const_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
{
return c4x_K_constant (op);
}
int
not_const_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
{
return c4x_N_constant (op);
}
int
reg_operand (rtx op, enum machine_mode mode)
{
if (GET_CODE (op) == SUBREG
&& GET_MODE (op) == QFmode)
return 0;
return register_operand (op, mode);
}
int
mixed_subreg_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
{
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;
}
int
r0r1_reg_operand (rtx op, enum machine_mode mode)
{
if (! reg_operand (op, mode))
return 0;
if (GET_CODE (op) == SUBREG)
op = SUBREG_REG (op);
return REG_P (op) && IS_R0R1_OR_PSEUDO_REG (op);
}
int
r2r3_reg_operand (rtx op, enum machine_mode mode)
{
if (! reg_operand (op, mode))
return 0;
if (GET_CODE (op) == SUBREG)
op = SUBREG_REG (op);
return REG_P (op) && IS_R2R3_OR_PSEUDO_REG (op);
}
int
ext_low_reg_operand (rtx op, enum machine_mode mode)
{
if (! reg_operand (op, mode))
return 0;
if (GET_CODE (op) == SUBREG)
op = SUBREG_REG (op);
return REG_P (op) && IS_EXT_LOW_OR_PSEUDO_REG (op);
}
int
ext_reg_operand (rtx op, enum machine_mode mode)
{
if (! reg_operand (op, mode))
return 0;
if (GET_CODE (op) == SUBREG)
op = SUBREG_REG (op);
if (! REG_P (op))
return 0;
return IS_EXT_OR_PSEUDO_REG (op);
}
int
std_reg_operand (rtx op, enum machine_mode mode)
{
if (! reg_operand (op, mode))
return 0;
if (GET_CODE (op) == SUBREG)
op = SUBREG_REG (op);
return REG_P (op) && IS_STD_OR_PSEUDO_REG (op);
}
int
std_or_reg_operand (rtx op, enum machine_mode mode)
{
if (reload_in_progress)
return std_reg_operand (op, mode);
return reg_operand (op, mode);
}
int
addr_reg_operand (rtx op, enum machine_mode mode)
{
if (! reg_operand (op, mode))
return 0;
return c4x_a_register (op);
}
int
index_reg_operand (rtx op, enum machine_mode mode)
{
if (! reg_operand (op, mode))
return 0;
if (GET_CODE (op) == SUBREG)
op = SUBREG_REG (op);
return c4x_x_register (op);
}
int
dp_reg_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
{
return REG_P (op) && IS_DP_OR_PSEUDO_REG (op);
}
int
sp_reg_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
{
return REG_P (op) && IS_SP_OR_PSEUDO_REG (op);
}
int
st_reg_operand (register rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
{
return REG_P (op) && IS_ST_OR_PSEUDO_REG (op);
}
int
rc_reg_operand (register rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
{
return REG_P (op) && IS_RC_OR_PSEUDO_REG (op);
}
int
call_address_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
{
return (REG_P (op) || symbolic_address_operand (op, mode));
}
int
symbolic_address_operand (register rtx op,
enum machine_mode mode ATTRIBUTE_UNUSED)
{
switch (GET_CODE (op))
{
case CONST:
case SYMBOL_REF:
case LABEL_REF:
return 1;
default:
return 0;
}
}
int
dst_operand (rtx op, enum machine_mode mode)
{
if (GET_CODE (op) == SUBREG
&& mixed_subreg_operand (op, mode))
return 0;
if (REG_P (op))
return reg_operand (op, mode);
return nonimmediate_operand (op, mode);
}
int
src_operand (rtx op, enum machine_mode mode)
{
if (GET_CODE (op) == SUBREG
&& mixed_subreg_operand (op, mode))
return 0;
if (REG_P (op))
return reg_operand (op, mode);
if (mode == VOIDmode)
mode = GET_MODE (op);
if (GET_CODE (op) == CONST_INT)
return (mode == QImode || mode == Pmode || mode == HImode)
&& c4x_I_constant (op);
if (GET_CODE (op) == CONST_DOUBLE)
return c4x_H_constant (op);
if (GET_CODE (op) == SYMBOL_REF
|| GET_CODE (op) == LABEL_REF
|| GET_CODE (op) == CONST)
return 0;
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);
}
int
src_hi_operand (rtx op, enum machine_mode mode)
{
if (c4x_O_constant (op))
return 1;
return src_operand (op, mode);
}
int
lsrc_operand (rtx op, enum machine_mode mode)
{
if (mode == VOIDmode)
mode = GET_MODE (op);
if (mode != QImode && mode != Pmode)
fatal_insn ("mode not QImode", op);
if (GET_CODE (op) == CONST_INT)
return c4x_L_constant (op) || c4x_J_constant (op);
return src_operand (op, mode);
}
int
tsrc_operand (rtx op, enum machine_mode mode)
{
if (mode == VOIDmode)
mode = GET_MODE (op);
if (mode != QImode && mode != Pmode)
fatal_insn ("mode not QImode", op);
if (GET_CODE (op) == CONST_INT)
return c4x_L_constant (op) || c4x_N_constant (op) || c4x_J_constant (op);
return src_operand (op, mode);
}
int
nonimmediate_src_operand (rtx op, enum machine_mode mode)
{
if (GET_CODE (op) == CONST_INT || GET_CODE (op) == CONST_DOUBLE)
return 0;
return src_operand (op, mode);
}
int
nonimmediate_lsrc_operand (rtx op, enum machine_mode mode)
{
if (GET_CODE (op) == CONST_INT || GET_CODE (op) == CONST_DOUBLE)
return 0;
return lsrc_operand (op, mode);
}
int
reg_or_const_operand (rtx op, enum machine_mode mode)
{
return reg_operand (op, mode) || const_operand (op, mode);
}
int
par_ind_operand (rtx op, enum machine_mode mode)
{
if (mode != VOIDmode && mode != GET_MODE (op))
return 0;
return c4x_S_indirect (op);
}
int
parallel_operand (rtx op, enum machine_mode mode)
{
return ext_low_reg_operand (op, mode) || par_ind_operand (op, mode);
}
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;
}
}
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 (! flag_argument_noalias)
{
if (MEM_VOLATILE_P (op0) || MEM_VOLATILE_P (op1))
return 1;
}
}
if (base0 == base1 && incdec0 && incdec0)
return 1;
if (! TARGET_DEVEL && base0 == base1 && (incdec0 || incdec1))
return 1;
if (base0 == base1 && disp0 == disp1 && index0 == index1)
return 1;
return 0;
}
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;
}
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);
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;
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);
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);
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);
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);
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);
if (GET_CODE (op1) == REG)
regs++;
if (GET_CODE (op2) == REG)
regs++;
if (regs != 1)
return 0;
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);
if (GET_CODE (op1) == REG)
regs++;
if (GET_CODE (op2) == REG)
regs++;
if (GET_CODE (op4) == REG)
regs++;
if (GET_CODE (op5) == REG)
regs++;
if (regs != 2)
return 0;
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;
}
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;
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);
}
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;
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;
return ! force || code == COMPARE || REGNO (op1) == REGNO (op0);
}
if (code == ASHIFTRT || code == LSHIFTRT
|| code == ASHIFT || code == COMPARE)
return code2 == REG
&& (c4x_S_indirect (op1) || c4x_R_indirect (op1));
if (code2 == REG)
{
switch (code1)
{
case CONST_INT:
break;
case CONST_DOUBLE:
if (! c4x_H_constant (op1))
return 0;
break;
case MEM:
break;
default:
abort ();
break;
}
if (GET_CODE (op0) == SCRATCH)
return 1;
if (!REG_P (op0))
return 0;
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)
{
return ! optimize || c4x_valid_operands (code, operands, mode, 0);
}
int
legitimize_operands (enum rtx_code code, rtx *operands, enum machine_mode mode)
{
if (code == COMPARE)
{
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;
}
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]);
if (! reload_in_progress
&& ! c4x_valid_operands (code, operands, mode, TARGET_FORCE))
{
operands[1] = force_reg (mode, operands[1]);
if (TARGET_FORCE)
{
emit_move_insn (operands[0], operands[1]);
operands[1] = copy_rtx (operands[0]);
}
else
{
if (! c4x_valid_operands (code, operands, mode, 0))
operands[2] = force_reg (mode, operands[2]);
}
}
if ((code == ASHIFTRT || code == LSHIFTRT)
&& (GET_CODE (operands[2]) != CONST_INT))
operands[2] = gen_rtx_NEG (mode, negate_rtx (mode, operands[2]));
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;
}
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;
}
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);
}
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:
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);
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;
void
c4x_global_label (const char *name)
{
struct name_list *p, *last;
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;
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;
}
}
void
c4x_external_ref (const char *name)
{
struct name_list *p;
p = extern_head;
while (p)
{
if (strcmp (p->name, name) == 0)
return;
p = p->next;
}
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;
}
static void
c4x_file_start (void)
{
int dspversion = 0;
if (TARGET_C30) dspversion = 30;
if (TARGET_C31) dspversion = 31;
if (TARGET_C32) dspversion = 32;
if (TARGET_C33) dspversion = 33;
if (TARGET_C40) dspversion = 40;
if (TARGET_C44) dspversion = 44;
default_file_start ();
fprintf (asm_out_file, "\t.version\t%d\n", dspversion);
fputs ("\n\t.data\ndata_sec:\n", asm_out_file);
}
static void
c4x_file_end (void)
{
struct name_list *p;
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;
}
}
const struct attribute_spec c4x_attribute_table[] =
{
{ "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 }
};
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 ("%qs attribute only applies to functions",
IDENTIFIER_POINTER (name));
*no_add_attrs = true;
}
return NULL_TREE;
}
int
c4x_rptb_rpts_p (rtx insn, rtx op)
{
insn = NEXT_INSN (insn);
if (GET_CODE (insn) != CODE_LABEL)
{
if (TARGET_DEBUG)
fatal_insn("c4x_rptb_rpts_p: Repeat block top label moved\n",
insn);
return 0;
}
insn = next_nonnote_insn (insn);
if (! INSN_P (insn))
return 0;
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));
}
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;
}
int
c4x_check_laj_p (rtx insn)
{
insn = prev_nonnote_insn (insn);
if (insn == 0)
return 0;
if (GET_CODE (insn) == CODE_LABEL)
return 1;
if (c4x_r11_set_p (insn))
return 1;
return 0;
}
#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)
{
if (flag_schedule_insns == 0 && ! reload_completed)
return 0;
if (recog_memoized (dep_insn) < 0)
return 0;
if (REG_NOTE_KIND (link) == 0)
{
int max = 0;
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
{
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;
return cost;
}
else if (REG_NOTE_KIND (link) == REG_DEP_ANTI)
{
return 0;
}
else if (REG_NOTE_KIND (link) == REG_DEP_OUTPUT)
{
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)
static bool
c4x_rtx_costs (rtx x, int code, int outer_code, int *total)
{
HOST_WIDE_INT val;
switch (code)
{
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;
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;
}
}
static void
c4x_external_libcall (rtx fun)
{
c4x_external_ref (XSTR (fun, 0));
}
static rtx
c4x_struct_value_rtx (tree fntype ATTRIBUTE_UNUSED,
int incoming ATTRIBUTE_UNUSED)
{
return gen_rtx_REG (Pmode, AR0_REGNO);
}