#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "tm.h"
#include "rtl.h"
#include "regs.h"
#include "hard-reg-set.h"
#include "real.h"
#include "insn-config.h"
#include "conditions.h"
#include "insn-attr.h"
#include "flags.h"
#include "tree.h"
#include "expr.h"
#include "except.h"
#include "function.h"
#include "toplev.h"
#include "recog.h"
#include "tm_p.h"
#include "debug.h"
#include "output.h"
#include "target.h"
#include "target-def.h"
#include "ggc.h"
#include "optabs.h"
#define ADDITIVE_SIZE_MODIFIER(size) \
((size) <= 63 ? "q" : (size) <= 255 ? "u.b" : (size) <= 65535 ? "u.w" : ".d")
#define ASSERT_PLT_UNSPEC(x) \
do \
{ \
if (XEXP (x, 1) != NULL_RTX \
|| (GET_CODE (XVECEXP (x, 0, 0)) != SYMBOL_REF \
&& GET_CODE (XVECEXP (x, 0, 0)) != LABEL_REF)) \
abort (); \
} while (0)
#define LOSE_AND_RETURN(msgid, x) \
do \
{ \
cris_operand_lossage (msgid, x); \
return; \
} while (0)
struct machine_function GTY(())
{
int needs_return_address_on_stack;
};
static char cris_output_insn_is_bound = 0;
static int cris_pic_sympart_only = 0;
static int in_code = 0;
static int cris_reg_overlap_mentioned_p (rtx, rtx);
static void cris_print_base (rtx, FILE *);
static void cris_print_index (rtx, FILE *);
static void cris_output_addr_const (FILE *, rtx);
static struct machine_function * cris_init_machine_status (void);
static rtx cris_struct_value_rtx (tree, int);
static void cris_setup_incoming_varargs (CUMULATIVE_ARGS *, enum machine_mode,
tree type, int *, int);
static int cris_initial_frame_pointer_offset (void);
static int saved_regs_mentioned (rtx);
static void cris_target_asm_function_prologue (FILE *, HOST_WIDE_INT);
static void cris_target_asm_function_epilogue (FILE *, HOST_WIDE_INT);
static void cris_operand_lossage (const char *, rtx);
static void cris_asm_output_mi_thunk
(FILE *, tree, HOST_WIDE_INT, HOST_WIDE_INT, tree);
static void cris_file_start (void);
static void cris_init_libfuncs (void);
static bool cris_rtx_costs (rtx, int, int, int *);
static int cris_address_cost (rtx);
static bool cris_pass_by_reference (CUMULATIVE_ARGS *, enum machine_mode,
tree, bool);
static int cris_arg_partial_bytes (CUMULATIVE_ARGS *, enum machine_mode,
tree, bool);
static char save_last[80];
const char *cris_max_stackframe_str;
const char *cris_cpu_str;
const char *cris_tune_str;
const char *cris_elinux_stacksize_str;
int cris_max_stackframe = 0;
int cris_cpu_version = CRIS_DEFAULT_CPU_VERSION;
#undef TARGET_ASM_ALIGNED_HI_OP
#define TARGET_ASM_ALIGNED_HI_OP "\t.word\t"
#undef TARGET_ASM_ALIGNED_SI_OP
#define TARGET_ASM_ALIGNED_SI_OP "\t.dword\t"
#undef TARGET_ASM_ALIGNED_DI_OP
#define TARGET_ASM_ALIGNED_DI_OP "\t.quad\t"
#undef TARGET_ASM_UNALIGNED_HI_OP
#define TARGET_ASM_UNALIGNED_HI_OP TARGET_ASM_ALIGNED_HI_OP
#undef TARGET_ASM_UNALIGNED_SI_OP
#define TARGET_ASM_UNALIGNED_SI_OP TARGET_ASM_ALIGNED_SI_OP
#undef TARGET_ASM_UNALIGNED_DI_OP
#define TARGET_ASM_UNALIGNED_DI_OP TARGET_ASM_ALIGNED_DI_OP
#undef TARGET_ASM_FUNCTION_PROLOGUE
#define TARGET_ASM_FUNCTION_PROLOGUE cris_target_asm_function_prologue
#undef TARGET_ASM_FUNCTION_EPILOGUE
#define TARGET_ASM_FUNCTION_EPILOGUE cris_target_asm_function_epilogue
#undef TARGET_ASM_OUTPUT_MI_THUNK
#define TARGET_ASM_OUTPUT_MI_THUNK cris_asm_output_mi_thunk
#undef TARGET_ASM_CAN_OUTPUT_MI_THUNK
#define TARGET_ASM_CAN_OUTPUT_MI_THUNK default_can_output_mi_thunk_no_vcall
#undef TARGET_ASM_FILE_START
#define TARGET_ASM_FILE_START cris_file_start
#undef TARGET_INIT_LIBFUNCS
#define TARGET_INIT_LIBFUNCS cris_init_libfuncs
#undef TARGET_RTX_COSTS
#define TARGET_RTX_COSTS cris_rtx_costs
#undef TARGET_ADDRESS_COST
#define TARGET_ADDRESS_COST cris_address_cost
#undef TARGET_PROMOTE_FUNCTION_ARGS
#define TARGET_PROMOTE_FUNCTION_ARGS hook_bool_tree_true
#undef TARGET_STRUCT_VALUE_RTX
#define TARGET_STRUCT_VALUE_RTX cris_struct_value_rtx
#undef TARGET_SETUP_INCOMING_VARARGS
#define TARGET_SETUP_INCOMING_VARARGS cris_setup_incoming_varargs
#undef TARGET_PASS_BY_REFERENCE
#define TARGET_PASS_BY_REFERENCE cris_pass_by_reference
#undef TARGET_ARG_PARTIAL_BYTES
#define TARGET_ARG_PARTIAL_BYTES cris_arg_partial_bytes
struct gcc_target targetm = TARGET_INITIALIZER;
int
cris_bdap_operand (rtx op, enum machine_mode mode)
{
register enum rtx_code code = GET_CODE (op);
if (mode != SImode && (mode != VOIDmode || GET_MODE (op) != VOIDmode))
return 0;
if (register_operand (op, mode)
|| (CONSTANT_P (op) && !(flag_pic && cris_symbol (op))))
return 1;
if (code == MEM)
{
rtx tem = XEXP (op, 0);
if (mode == SImode
&& (register_operand (tem, SImode)
|| (GET_CODE (tem) == POST_INC
&& register_operand (XEXP (tem, 0), SImode))))
return 1;
else
return 0;
}
if (code == SIGN_EXTEND)
{
rtx tem = XEXP (op, 0);
if (GET_CODE (tem) != MEM)
return 0;
tem = XEXP (tem, 0);
if (mode == SImode
&& (register_operand (tem, SImode)
|| (GET_CODE (tem) == POST_INC
&& register_operand (XEXP (tem, 0), SImode))))
return 1;
else
return 0;
}
return 0;
}
int
cris_bdap_biap_operand (rtx op, enum machine_mode mode)
{
register enum rtx_code code = GET_CODE (op);
rtx reg;
rtx val;
if (cris_bdap_operand (op, mode))
return 1;
if (mode != SImode && (mode != VOIDmode || GET_MODE (op) != VOIDmode))
return 0;
if (code != MULT)
return 0;
if (GET_CODE (XEXP (op, 0)) == CONST_INT)
{
val = XEXP (op, 0);
reg = XEXP (op, 1);
}
else
{
val = XEXP (op, 1);
reg = XEXP (op, 0);
}
if (! register_operand (reg, SImode) || GET_CODE (val) != CONST_INT)
return 0;
if ((code == MULT
&& (INTVAL (val) == 1 || INTVAL (val) == 2 || INTVAL (val) == 4)))
return 1;
return 0;
}
int
cris_orthogonal_operator (rtx x, enum machine_mode mode)
{
enum rtx_code code = GET_CODE (x);
if (mode == VOIDmode)
mode = GET_MODE (x);
return (GET_MODE (x) == mode
&& (code == PLUS || code == MINUS
|| code == IOR || code == AND || code == UMIN));
}
int
cris_commutative_orth_op (rtx x, enum machine_mode mode)
{
enum rtx_code code = GET_CODE (x);
if (mode == VOIDmode)
mode = GET_MODE (x);
return (GET_MODE (x) == mode &&
(code == PLUS
|| code == IOR || code == AND || code == UMIN));
}
int
cris_operand_extend_operator (rtx x, enum machine_mode mode)
{
enum rtx_code code = GET_CODE (x);
if (mode == VOIDmode)
mode = GET_MODE (x);
return (GET_MODE (x) == mode
&& (code == PLUS || code == MINUS || code == UMIN));
}
int
cris_additive_operand_extend_operator (rtx x, enum machine_mode mode)
{
enum rtx_code code = GET_CODE (x);
if (mode == VOIDmode)
mode = GET_MODE (x);
return (GET_MODE (x) == mode
&& (code == PLUS || code == MINUS));
}
int
cris_extend_operator (rtx x, enum machine_mode mode)
{
enum rtx_code code = GET_CODE (x);
if (mode == VOIDmode)
mode = GET_MODE (x);
return
(GET_MODE (x) == mode && (code == SIGN_EXTEND || code == ZERO_EXTEND));
}
int
cris_plus_or_bound_operator (rtx x, enum machine_mode mode)
{
enum rtx_code code = GET_CODE (x);
if (mode == VOIDmode)
mode = GET_MODE (x);
return
(GET_MODE (x) == mode && (code == UMIN || code == PLUS));
}
int
cris_mem_op (rtx x, enum machine_mode mode)
{
if (mode == VOIDmode)
mode = GET_MODE (x);
return GET_MODE (x) == mode && GET_CODE (x) == MEM;
}
int
cris_general_operand_or_symbol (rtx op, enum machine_mode mode)
{
return general_operand (op, mode)
|| (CONSTANT_P (op) && cris_symbol (op));
}
int
cris_general_operand_or_gotless_symbol (rtx op, enum machine_mode mode)
{
return general_operand (op, mode)
|| (CONSTANT_P (op) && cris_gotless_symbol (op));
}
int
cris_general_operand_or_plt_symbol (rtx op, enum machine_mode mode)
{
return general_operand (op, mode)
|| (GET_CODE (op) == CONST
&& GET_CODE (XEXP (op, 0)) == UNSPEC
&& !TARGET_AVOID_GOTPLT);
}
int
cris_mem_call_operand (rtx op, enum machine_mode mode)
{
rtx xmem;
if (GET_CODE (op) != MEM)
return 0;
if (memory_operand (op, mode))
return 1;
xmem = XEXP (op, 0);
return cris_general_operand_or_symbol (xmem, GET_MODE (op));
}
void
cris_conditional_register_usage (void)
{
if (flag_pic)
fixed_regs[PIC_OFFSET_TABLE_REGNUM]
= call_used_regs[PIC_OFFSET_TABLE_REGNUM] = 1;
}
int
cris_cfun_uses_pic_table (void)
{
return current_function_uses_pic_offset_table;
}
const char *
cris_op_str (rtx x)
{
cris_output_insn_is_bound = 0;
switch (GET_CODE (x))
{
case PLUS:
return "add";
break;
case MINUS:
return "sub";
break;
case MULT:
abort ();
break;
case DIV:
return "div";
break;
case AND:
return "and";
break;
case IOR:
return "or";
break;
case XOR:
return "xor";
break;
case NOT:
return "not";
break;
case ASHIFT:
return "lsl";
break;
case LSHIFTRT:
return "lsr";
break;
case ASHIFTRT:
return "asr";
break;
case UMIN:
cris_output_insn_is_bound = 1;
return "bound";
break;
default:
return "Unknown operator";
break;
}
}
static void
cris_operand_lossage (const char *msgid, rtx op)
{
debug_rtx (op);
output_operand_lossage ("%s", msgid);
}
static void
cris_print_index (rtx index, FILE *file)
{
rtx inner = XEXP (index, 0);
if (GET_CODE (index) != CONST_INT || INTVAL (index) >= 0)
putc ('+', file);
if (REG_P (index))
fprintf (file, "$%s.b", reg_names[REGNO (index)]);
else if (CONSTANT_P (index))
cris_output_addr_const (file, index);
else if (GET_CODE (index) == MULT)
{
fprintf (file, "$%s.",
reg_names[REGNO (XEXP (index, 0))]);
putc (INTVAL (XEXP (index, 1)) == 2 ? 'w' : 'd', file);
}
else if (GET_CODE (index) == SIGN_EXTEND &&
GET_CODE (inner) == MEM)
{
rtx inner_inner = XEXP (inner, 0);
if (GET_CODE (inner_inner) == POST_INC)
{
fprintf (file, "[$%s+].",
reg_names[REGNO (XEXP (inner_inner, 0))]);
putc (GET_MODE (inner) == HImode ? 'w' : 'b', file);
}
else
{
fprintf (file, "[$%s].", reg_names[REGNO (inner_inner)]);
putc (GET_MODE (inner) == HImode ? 'w' : 'b', file);
}
}
else if (GET_CODE (index) == MEM)
{
if (GET_CODE (inner) == POST_INC)
fprintf (file, "[$%s+].d", reg_names[REGNO (XEXP (inner, 0))]);
else
fprintf (file, "[$%s].d", reg_names[REGNO (inner)]);
}
else
cris_operand_lossage ("unexpected index-type in cris_print_index",
index);
}
static void
cris_print_base (rtx base, FILE *file)
{
if (REG_P (base))
fprintf (file, "$%s", reg_names[REGNO (base)]);
else if (GET_CODE (base) == POST_INC)
fprintf (file, "$%s+", reg_names[REGNO (XEXP (base, 0))]);
else
cris_operand_lossage ("unexpected base-type in cris_print_base",
base);
}
int
cris_fatal (char *arg)
{
internal_error (arg);
return 0;
}
static GTY(()) unsigned long cfa_label_num = 0;
static void
cris_target_asm_function_prologue (FILE *file, HOST_WIDE_INT size)
{
int regno;
int cfoa_size = current_function_outgoing_args_size;
int last_movem_reg = -1;
int doing_dwarf = dwarf2out_do_frame ();
int framesize;
int faked_args_size = 0;
int cfa_write_offset = 0;
static char cfa_label[30];
int return_address_on_stack
= regs_ever_live[CRIS_SRP_REGNUM]
|| cfun->machine->needs_return_address_on_stack != 0;
if (!TARGET_PROLOGUE_EPILOGUE)
return;
if (size < 0)
abort ();
if (TARGET_STACK_ALIGN)
size = TARGET_ALIGN_BY_32 ? (size + 3) & ~3 : (size + 1) & ~1;
if (current_function_pretend_args_size)
{
int pretend = current_function_pretend_args_size;
for (regno = CRIS_FIRST_ARG_REG + CRIS_MAX_ARGS_IN_REGS - 1;
pretend > 0;
regno--, pretend -= 4)
{
fprintf (file, "\tpush $%s\n", reg_names[regno]);
faked_args_size += 4;
}
}
framesize = faked_args_size;
if (doing_dwarf)
{
int cfa_offset
= faked_args_size
+ (return_address_on_stack ? 4 : 0)
+ (frame_pointer_needed ? 4 : 0);
int cfa_reg;
if (frame_pointer_needed)
cfa_reg = FRAME_POINTER_REGNUM;
else
{
cfa_reg = STACK_POINTER_REGNUM;
cfa_offset += cris_initial_frame_pointer_offset ();
}
ASM_GENERATE_INTERNAL_LABEL (cfa_label, "LCFIT",
cfa_label_num++);
dwarf2out_def_cfa (cfa_label, cfa_reg, cfa_offset);
cfa_write_offset = - faked_args_size - 4;
}
if (return_address_on_stack)
{
fprintf (file, "\tPush $srp\n");
framesize += 4;
if (doing_dwarf)
{
dwarf2out_return_save (cfa_label, cfa_write_offset);
cfa_write_offset -= 4;
}
}
if (frame_pointer_needed)
{
fprintf (file, "\tpush $%s\n\tmove.d $sp,$%s\n",
reg_names[FRAME_POINTER_REGNUM],
reg_names[FRAME_POINTER_REGNUM]);
framesize += 4;
if (doing_dwarf)
{
dwarf2out_reg_save (cfa_label, FRAME_POINTER_REGNUM,
cfa_write_offset);
cfa_write_offset -= 4;
}
}
cfa_write_offset -= size;
for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
{
if ((((regs_ever_live[regno]
&& !call_used_regs[regno])
|| (regno == (int) PIC_OFFSET_TABLE_REGNUM
&& (current_function_uses_pic_offset_table
|| (flag_pic
&& regs_ever_live[regno + 1]
&& !call_used_regs[regno + 1]))))
&& (regno != FRAME_POINTER_REGNUM || !frame_pointer_needed)
&& regno != CRIS_SRP_REGNUM)
|| (current_function_calls_eh_return
&& (regno == EH_RETURN_DATA_REGNO (0)
|| regno == EH_RETURN_DATA_REGNO (1)
|| regno == EH_RETURN_DATA_REGNO (2)
|| regno == EH_RETURN_DATA_REGNO (3))))
{
if (regno == last_movem_reg + 1)
last_movem_reg++;
else
{
if (last_movem_reg != -1)
{
if ((last_movem_reg + 1) * 4 + size >= 64
&& (last_movem_reg + 1) * 4 + size <= 128
&& cris_cpu_version >= CRIS_CPU_SVINTO
&& TARGET_SIDE_EFFECT_PREFIXES)
fprintf (file, "\tmovem $%s,[$sp=$sp-"HOST_WIDE_INT_PRINT_DEC"]\n",
reg_names[last_movem_reg],
(last_movem_reg + 1) * 4 + size);
else
{
fprintf (file, "\tsub%s "HOST_WIDE_INT_PRINT_DEC",$sp\n",
ADDITIVE_SIZE_MODIFIER ((last_movem_reg + 1)
* 4 + size),
(last_movem_reg + 1) * 4 + size);
fprintf (file, "\tmovem $%s,[$sp]\n",
reg_names[last_movem_reg]);
}
framesize += (last_movem_reg + 1) * 4 + size;
if (TARGET_PDEBUG)
fprintf (file, "; frame "HOST_WIDE_INT_PRINT_DEC
", #regs %d, bytes %d args %d\n",
size,
last_movem_reg + 1,
(last_movem_reg + 1) * 4,
current_function_args_size);
last_movem_reg = -1;
size = 0;
}
else if (size > 0)
{
fprintf (file, "\tSub%s "HOST_WIDE_INT_PRINT_DEC",$sp\n",
ADDITIVE_SIZE_MODIFIER (size),
size);
framesize += size;
size = 0;
}
fprintf (file, "\tPush $%s\n", reg_names[regno]);
framesize += 4;
}
if (doing_dwarf)
{
dwarf2out_reg_save (cfa_label, regno, cfa_write_offset);
cfa_write_offset -= 4;
}
}
}
if (last_movem_reg != -1)
{
if ((last_movem_reg + 1) * 4 + size >= 64
&& (last_movem_reg + 1) * 4 + size <= 128
&& cris_cpu_version >= CRIS_CPU_SVINTO
&& TARGET_SIDE_EFFECT_PREFIXES)
fprintf (file, "\tmovem $%s,[$sp=$sp-"HOST_WIDE_INT_PRINT_DEC"]\n",
reg_names[last_movem_reg],
(last_movem_reg+1) * 4 + size);
else
{
fprintf (file, "\tsub%s "HOST_WIDE_INT_PRINT_DEC",$sp\n",
ADDITIVE_SIZE_MODIFIER ((last_movem_reg + 1) * 4 + size),
(last_movem_reg + 1) * 4 + size);
fprintf (file, "\tmovem $%s,[$sp]\n", reg_names[last_movem_reg]);
}
framesize += (last_movem_reg + 1) * 4 + size;
if (TARGET_PDEBUG)
fprintf (file, "; frame "HOST_WIDE_INT_PRINT_DEC
", #regs %d, bytes %d args %d\n",
size,
last_movem_reg + 1,
(last_movem_reg + 1) * 4,
current_function_args_size);
if (cfoa_size)
{
fprintf (file, "\tSub%s %d,$sp\n",
ADDITIVE_SIZE_MODIFIER (cfoa_size),
cfoa_size);
framesize += cfoa_size;
}
}
else if ((size + cfoa_size) > 0)
{
fprintf (file, "\tSub%s "HOST_WIDE_INT_PRINT_DEC",$sp\n",
ADDITIVE_SIZE_MODIFIER (size + cfoa_size),
cfoa_size + size);
framesize += size + cfoa_size;
}
if (current_function_uses_pic_offset_table)
fprintf (file, "\tmove.d $pc,$%s\n\tsub.d .:GOTOFF,$%s\n",
reg_names[PIC_OFFSET_TABLE_REGNUM],
reg_names[PIC_OFFSET_TABLE_REGNUM]);
if (doing_dwarf)
ASM_OUTPUT_LABEL (file, cfa_label);
if (TARGET_PDEBUG)
fprintf (file,
"; parm #%d @ %d; frame " HOST_WIDE_INT_PRINT_DEC
", FP-SP is %d; leaf: %s%s; fp %s, outg: %d arg %d\n",
CRIS_MAX_ARGS_IN_REGS + 1, FIRST_PARM_OFFSET (0),
get_frame_size (),
cris_initial_frame_pointer_offset (),
leaf_function_p () ? "yes" : "no",
return_address_on_stack ? "no" :"yes",
frame_pointer_needed ? "yes" : "no",
cfoa_size, current_function_args_size);
if (cris_max_stackframe && framesize > cris_max_stackframe)
warning ("stackframe too big: %d bytes", framesize);
}
static int
saved_regs_mentioned (rtx x)
{
int i;
const char *fmt;
RTX_CODE code;
code = GET_CODE (x);
switch (code)
{
case REG:
i = REGNO (x);
return !call_used_regs[i];
case SUBREG:
i = REGNO (SUBREG_REG (x));
return !call_used_regs[i];
default:
;
}
fmt = GET_RTX_FORMAT (code);
for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
{
if (fmt[i] == 'e')
{
if (saved_regs_mentioned (XEXP (x, i)))
return 1;
}
else if (fmt[i] == 'E')
{
int j;
for (j = XVECLEN (x, i) - 1; j >=0; j--)
if (saved_regs_mentioned (XEXP (x, i)))
return 1;
}
}
return 0;
}
int
cris_eligible_for_epilogue_delay (rtx insn)
{
if (get_attr_slottable (insn) != SLOTTABLE_YES)
return 0;
if (reg_mentioned_p (stack_pointer_rtx, PATTERN (insn)))
return 0;
if (frame_pointer_needed
&& reg_mentioned_p (frame_pointer_rtx, PATTERN (insn)))
return 0;
if (saved_regs_mentioned (PATTERN (insn)))
return 0;
return 1;
}
int
cris_delay_slots_for_epilogue (void)
{
if (regs_ever_live[CRIS_SRP_REGNUM]
|| cfun->machine->needs_return_address_on_stack != 0)
return 0;
cris_target_asm_function_epilogue (NULL, get_frame_size ());
if (*save_last)
return 1;
return 0;
}
static void
cris_target_asm_function_epilogue (FILE *file, HOST_WIDE_INT size)
{
int regno;
int last_movem_reg = -1;
rtx insn = get_last_insn ();
int argspace_offset = current_function_outgoing_args_size;
int pretend = current_function_pretend_args_size;
int return_address_on_stack
= regs_ever_live[CRIS_SRP_REGNUM]
|| cfun->machine->needs_return_address_on_stack != 0;
save_last[0] = 0;
if (file && !TARGET_PROLOGUE_EPILOGUE)
return;
if (TARGET_PDEBUG && file)
fprintf (file, ";;\n");
if (TARGET_STACK_ALIGN)
size = TARGET_ALIGN_BY_32 ? (size + 3) & ~3 : (size + 1) & ~1;
if (GET_CODE (insn) == NOTE)
insn = prev_nonnote_insn (insn);
if (insn
&& (GET_CODE (insn) == BARRIER
|| (GET_CODE (insn) == JUMP_INSN
&& GET_CODE (PATTERN (insn)) == RETURN)))
{
if (TARGET_PDEBUG && file)
fprintf (file, ";;;;;\n");
return;
}
for (regno = 0;
regno < FIRST_PSEUDO_REGISTER;
regno++)
if ((((regs_ever_live[regno]
&& !call_used_regs[regno])
|| (regno == (int) PIC_OFFSET_TABLE_REGNUM
&& (current_function_uses_pic_offset_table
|| (flag_pic
&& regs_ever_live[regno + 1]
&& !call_used_regs[regno + 1]))))
&& (regno != FRAME_POINTER_REGNUM || !frame_pointer_needed)
&& regno != CRIS_SRP_REGNUM)
|| (current_function_calls_eh_return
&& (regno == EH_RETURN_DATA_REGNO (0)
|| regno == EH_RETURN_DATA_REGNO (1)
|| regno == EH_RETURN_DATA_REGNO (2)
|| regno == EH_RETURN_DATA_REGNO (3))))
{
if (regno == last_movem_reg + 1)
last_movem_reg++;
else
break;
}
for (regno = FIRST_PSEUDO_REGISTER - 1;
regno > last_movem_reg;
regno--)
if ((((regs_ever_live[regno]
&& !call_used_regs[regno])
|| (regno == (int) PIC_OFFSET_TABLE_REGNUM
&& (current_function_uses_pic_offset_table
|| (flag_pic
&& regs_ever_live[regno + 1]
&& !call_used_regs[regno + 1]))))
&& (regno != FRAME_POINTER_REGNUM || !frame_pointer_needed)
&& regno != CRIS_SRP_REGNUM)
|| (current_function_calls_eh_return
&& (regno == EH_RETURN_DATA_REGNO (0)
|| regno == EH_RETURN_DATA_REGNO (1)
|| regno == EH_RETURN_DATA_REGNO (2)
|| regno == EH_RETURN_DATA_REGNO (3))))
{
if (argspace_offset)
{
if (file)
fprintf (file, "\tAdd%s %d,$sp\n",
ADDITIVE_SIZE_MODIFIER (argspace_offset),
argspace_offset);
argspace_offset = 0;
}
if (*save_last && file)
fprintf (file, save_last);
sprintf (save_last, "\tPop $%s\n", reg_names[regno]);
}
if (last_movem_reg != -1)
{
if (argspace_offset)
{
if (*save_last && file)
{
fprintf (file, save_last);
*save_last = 0;
}
if (file)
fprintf (file, "\tAdd%s %d,$sp\n",
ADDITIVE_SIZE_MODIFIER (argspace_offset),
argspace_offset);
argspace_offset = 0;
}
else if (*save_last && file)
fprintf (file, save_last);
sprintf (save_last, "\tmovem [$sp+],$%s\n", reg_names[last_movem_reg]);
}
if (frame_pointer_needed)
{
if (*save_last && file)
fprintf (file, save_last);
if (file)
fprintf (file, "\tmove.d $%s,$sp\n",
reg_names[FRAME_POINTER_REGNUM]);
sprintf (save_last, "\tPop $%s\n",
reg_names[FRAME_POINTER_REGNUM]);
}
else
{
size += argspace_offset;
if (size)
{
if (*save_last && file)
fprintf (file, save_last);
sprintf (save_last, "\tadd%s "HOST_WIDE_INT_PRINT_DEC",$sp\n",
ADDITIVE_SIZE_MODIFIER (size), size);
}
if (size > 63)
{
if (file)
fprintf (file, save_last);
*save_last = 0;
}
}
if (return_address_on_stack && pretend == 0)
{
if (*save_last && file)
fprintf (file, save_last);
*save_last = 0;
if (file)
{
if (current_function_calls_eh_return)
{
fprintf (file, "\tpop $srp\n");
fprintf (file, "\tret\n");
fprintf (file, "\tadd.d $%s,$sp\n", reg_names[CRIS_STACKADJ_REG]);
}
else
fprintf (file, "\tJump [$sp+]\n");
if (current_function_epilogue_delay_list)
internal_error ("allocated but unused delay list in epilogue");
}
return;
}
if (current_function_calls_eh_return)
internal_error ("unexpected function type needing stack adjustment for\
__builtin_eh_return");
if (pretend)
{
if (return_address_on_stack)
{
if (*save_last && file)
fprintf (file, save_last);
*save_last = 0;
if (file)
fprintf (file, "\tpop $srp\n");
}
if (*save_last && file)
fprintf (file, save_last);
sprintf (save_last, "\tadd%s %d,$sp\n",
ADDITIVE_SIZE_MODIFIER (pretend), pretend);
}
if (file && current_function_epilogue_delay_list)
{
if (*save_last)
fprintf (file, save_last);
fprintf (file, "\tRet\n");
final_scan_insn (XEXP (current_function_epilogue_delay_list, 0),
file, 1, -2, 1, NULL);
}
else if (file)
{
fprintf (file, "\tRet\n");
if (*save_last)
fprintf (file, save_last);
else
fprintf (file, "\tnOp\n");
}
}
void
cris_print_operand (FILE *file, rtx x, int code)
{
rtx operand = x;
static const char *const mults[] = { "BAD:0", ".b", ".w", "BAD:3", ".d" };
switch (code)
{
case 'b':
if (GET_CODE (x) != CONST_INT
|| ! CONST_OK_FOR_LETTER_P (INTVAL (x), 'O'))
LOSE_AND_RETURN ("invalid operand for 'b' modifier", x);
fprintf (file, HOST_WIDE_INT_PRINT_DEC,
INTVAL (x)| (INTVAL (x) <= 255 ? ~255 : ~65535));
return;
case 'x':
fprintf (file, "%s", cris_op_str (operand));
return;
case 'v':
if (! flag_pic || ! CONSTANT_P (x) || ! cris_gotless_symbol (x))
LOSE_AND_RETURN ("invalid operand for 'v' modifier", x);
cris_pic_sympart_only++;
cris_output_addr_const (file, x);
cris_pic_sympart_only--;
return;
case 'P':
if (! flag_pic || ! CONSTANT_P (x) || ! cris_gotless_symbol (x))
LOSE_AND_RETURN ("invalid operand for 'P' modifier", x);
fprintf (file, "$%s", reg_names [PIC_OFFSET_TABLE_REGNUM]);
return;
case 'p':
if (GET_CODE (x) != CONST_INT || exact_log2 (INTVAL (x)) < 0 )
LOSE_AND_RETURN ("invalid operand for 'p' modifier", x);
fprintf (file, "%d", exact_log2 (INTVAL (x)));
return;
case 's':
cris_output_insn_is_bound = 0;
if (GET_MODE (x) == VOIDmode && GET_CODE (x) == CONST_INT)
{
if (INTVAL (x) >= 0)
{
if (INTVAL (x) <= 255)
putc ('b', file);
else if (INTVAL (x) <= 65535)
putc ('w', file);
else
putc ('d', file);
}
else
putc ('d', file);
return;
}
putc ((GET_MODE (x) == SImode || GET_MODE (x) == SFmode)
? 'd' : GET_MODE (x) == HImode ? 'w'
: GET_MODE (x) == QImode ? 'b'
: 'X',
file);
return;
case 'z':
if (GET_CODE (x) != CONST_INT
|| INTVAL (x) < -32768 || INTVAL (x) > 65535)
LOSE_AND_RETURN ("invalid operand for 'z' modifier", x);
putc (INTVAL (x) >= -128 && INTVAL (x) <= 255 ? 'b' : 'w', file);
return;
case '#':
if (dbr_sequence_length () == 0)
fputs ("\n\tnop", file);
return;
case '!':
if (TARGET_MUL_BUG)
fputs (optimize_size
? ".p2alignw 2,0x050f\n\t"
: ".p2alignw 5,0x050f,2\n\t", file);
return;
case 'H':
switch (GET_CODE (operand))
{
case CONST_INT:
if (HOST_BITS_PER_WIDE_INT == 32)
fprintf (file, INTVAL (operand) < 0 ? "-1" : "0");
else
fprintf (file, "0x%x", (unsigned int)(INTVAL (x) >> 31 >> 1));
return;
case CONST_DOUBLE:
if (GET_MODE (operand) == VOIDmode)
{
fprintf (file, HOST_WIDE_INT_PRINT_HEX, CONST_DOUBLE_HIGH (x));
return;
}
else
LOSE_AND_RETURN ("invalid operand for 'H' modifier", x);
case REG:
if (REGNO (operand) > STACK_POINTER_REGNUM - 2)
LOSE_AND_RETURN ("bad register", operand);
fprintf (file, "$%s", reg_names[REGNO (operand) + 1]);
return;
case MEM:
{
rtx adj_mem = operand;
int size
= GET_MODE_BITSIZE (GET_MODE (operand)) / BITS_PER_UNIT;
if (GET_CODE (XEXP (adj_mem, 0)) != POST_INC)
adj_mem
= adjust_address (adj_mem, GET_MODE (adj_mem), size / 2);
output_address (XEXP (adj_mem, 0));
return;
}
default:
LOSE_AND_RETURN ("invalid operand for 'H' modifier", x);
}
case 'L':
operand = XEXP (operand, 0);
break;
case 'e':
if (GET_CODE (operand) != SIGN_EXTEND
&& GET_CODE (operand) != ZERO_EXTEND
&& GET_CODE (operand) != CONST_INT)
LOSE_AND_RETURN ("invalid operand for 'e' modifier", x);
if (cris_output_insn_is_bound)
{
cris_output_insn_is_bound = 0;
return;
}
putc (GET_CODE (operand) == SIGN_EXTEND
|| (GET_CODE (operand) == CONST_INT && INTVAL (operand) < 0)
? 's' : 'u', file);
return;
case 'm':
if (GET_CODE (operand) != SIGN_EXTEND && GET_CODE (operand) != ZERO_EXTEND)
LOSE_AND_RETURN ("invalid operand for 'm' modifier", x);
cris_print_operand (file, XEXP (operand, 0), 's');
return;
case 'M':
if (GET_CODE (operand) == CONST_DOUBLE)
{
fprintf (file, HOST_WIDE_INT_PRINT_HEX, CONST_DOUBLE_LOW (x));
return;
}
else if (HOST_BITS_PER_WIDE_INT > 32 && GET_CODE (operand) == CONST_INT)
{
fprintf (file, HOST_WIDE_INT_PRINT_HEX,
INTVAL (x) & ((unsigned int) 0x7fffffff * 2 + 1));
return;
}
break;
case 'A':
if (GET_CODE (operand) != CONST_INT)
LOSE_AND_RETURN ("invalid operand for 'A' modifier", x);
fprintf (file, INTVAL (operand) < 0 ? "adds.w" : "addq");
return;
case 'D':
if (GET_CODE (operand) != CONST_INT)
LOSE_AND_RETURN ("invalid operand for 'D' modifier", x);
fprintf (file, INTVAL (operand) < 0 ? "subs.w" : "subq");
return;
case 'S':
cris_print_index (operand, file);
return;
case 'T':
if (GET_CODE (operand) != CONST_INT || INTVAL (operand) > 4)
LOSE_AND_RETURN ("invalid operand for 'T' modifier", x);
fprintf (file, "%s", mults[INTVAL (operand)]);
return;
case 0:
break;
default:
LOSE_AND_RETURN ("invalid operand modifier letter", x);
}
switch (GET_CODE (operand))
{
case REG:
if (REGNO (operand) > 15)
internal_error ("internal error: bad register: %d", REGNO (operand));
fprintf (file, "$%s", reg_names[REGNO (operand)]);
return;
case MEM:
output_address (XEXP (operand, 0));
return;
case CONST_DOUBLE:
if (GET_MODE (operand) == VOIDmode)
output_addr_const (file, operand);
else
{
REAL_VALUE_TYPE r;
long l;
REAL_VALUE_FROM_CONST_DOUBLE (r, operand);
REAL_VALUE_TO_TARGET_SINGLE (r, l);
fprintf (file, "0x%lx", l);
}
return;
case UNSPEC:
ASSERT_PLT_UNSPEC (operand);
case CONST:
cris_output_addr_const (file, operand);
return;
case MULT:
case ASHIFT:
{
int i = GET_CODE (XEXP (operand, 1)) == CONST_INT
? INTVAL (XEXP (operand, 1)) : INTVAL (XEXP (operand, 0));
rtx reg = GET_CODE (XEXP (operand, 1)) == CONST_INT
? XEXP (operand, 0) : XEXP (operand, 1);
if (GET_CODE (reg) != REG
|| (GET_CODE (XEXP (operand, 0)) != CONST_INT
&& GET_CODE (XEXP (operand, 1)) != CONST_INT))
LOSE_AND_RETURN ("unexpected multiplicative operand", x);
cris_print_base (reg, file);
fprintf (file, ".%c",
i == 0 || (i == 1 && GET_CODE (operand) == MULT) ? 'b'
: i == 4 ? 'd'
: (i == 2 && GET_CODE (operand) == MULT) || i == 1 ? 'w'
: 'd');
return;
}
default:
if (CONSTANT_P (operand))
{
cris_output_addr_const (file, operand);
return;
}
LOSE_AND_RETURN ("unexpected operand", x);
}
}
void
cris_print_operand_address (FILE *file, rtx x)
{
putc ('[', file);
if (CONSTANT_ADDRESS_P (x))
cris_output_addr_const (file, x);
else if (BASE_OR_AUTOINCR_P (x))
cris_print_base (x, file);
else if (GET_CODE (x) == PLUS)
{
rtx x1, x2;
x1 = XEXP (x, 0);
x2 = XEXP (x, 1);
if (BASE_P (x1))
{
cris_print_base (x1, file);
cris_print_index (x2, file);
}
else if (BASE_P (x2))
{
cris_print_base (x2, file);
cris_print_index (x1, file);
}
else
LOSE_AND_RETURN ("unrecognized address", x);
}
else if (GET_CODE (x) == MEM)
{
putc ('[', file);
cris_print_base (XEXP (x, 0), file);
putc (']', file);
}
else
LOSE_AND_RETURN ("unrecognized address", x);
putc (']', file);
}
rtx
cris_return_addr_rtx (int count, rtx frameaddr ATTRIBUTE_UNUSED)
{
cfun->machine->needs_return_address_on_stack = 1;
return count == 0
? gen_rtx_MEM (Pmode, plus_constant (virtual_incoming_args_rtx, -4))
: NULL_RTX;
}
int
cris_return_address_on_stack ()
{
return cfun->machine->needs_return_address_on_stack;
}
static int
cris_initial_frame_pointer_offset (void)
{
int regno;
int offs = 0;
for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
if ((((regs_ever_live[regno]
&& !call_used_regs[regno])
|| (regno == (int) PIC_OFFSET_TABLE_REGNUM
&& (current_function_uses_pic_offset_table
|| (flag_pic
&& regs_ever_live[regno + 1]
&& !call_used_regs[regno + 1]))))
&& (regno != FRAME_POINTER_REGNUM || !frame_pointer_needed)
&& regno != CRIS_SRP_REGNUM)
|| (current_function_calls_eh_return
&& (regno == EH_RETURN_DATA_REGNO (0)
|| regno == EH_RETURN_DATA_REGNO (1)
|| regno == EH_RETURN_DATA_REGNO (2)
|| regno == EH_RETURN_DATA_REGNO (3))))
offs += 4;
offs += get_frame_size ();
offs += current_function_outgoing_args_size;
if (TARGET_STACK_ALIGN)
offs = TARGET_ALIGN_BY_32 ? (offs + 3) & ~3 : (offs + 1) & ~1;
return offs;
}
int
cris_initial_elimination_offset (int fromreg, int toreg)
{
int fp_sp_offset
= cris_initial_frame_pointer_offset ();
int return_address_on_stack
= regs_ever_live[CRIS_SRP_REGNUM]
|| cfun->machine->needs_return_address_on_stack != 0;
int ap_fp_offset = 4 + (return_address_on_stack ? 4 : 0);
if (fromreg == ARG_POINTER_REGNUM
&& toreg == FRAME_POINTER_REGNUM)
return ap_fp_offset;
if (fromreg == FRAME_POINTER_REGNUM
&& toreg == STACK_POINTER_REGNUM)
return fp_sp_offset;
if (fromreg == ARG_POINTER_REGNUM
&& toreg == STACK_POINTER_REGNUM)
return ap_fp_offset + fp_sp_offset - 4;
abort ();
}
void
cris_notice_update_cc (rtx exp, rtx insn)
{
if (TARGET_CCINIT)
{
CC_STATUS_INIT;
return;
}
switch (get_attr_cc (insn))
{
case CC_NONE:
if (GET_CODE (exp) == SET)
{
if (cc_status.value1
&& modified_in_p (cc_status.value1, insn))
cc_status.value1 = 0;
if (cc_status.value2
&& modified_in_p (cc_status.value2, insn))
cc_status.value2 = 0;
}
return;
case CC_CLOBBER:
CC_STATUS_INIT;
break;
case CC_NORMAL:
if (GET_CODE (exp) == SET)
{
if (SET_DEST (exp) == pc_rtx)
return;
if (SET_DEST (exp) == cc0_rtx)
{
cc_status.value1 = SET_SRC (exp);
cc_status.value2 = 0;
if (GET_CODE (SET_SRC (exp)) == ZERO_EXTRACT
&& XEXP (SET_SRC (exp), 1) == const1_rtx)
{
if (GET_CODE (XEXP (SET_SRC (exp), 0)) == CONST_INT)
cc_status.flags = CC_INVERTED;
else
cc_status.flags = CC_Z_IN_NOT_N;
}
else
cc_status.flags = 0;
if (GET_CODE (SET_SRC (exp)) == COMPARE)
{
if (!REG_P (XEXP (SET_SRC (exp), 0))
&& XEXP (SET_SRC (exp), 1) != const0_rtx)
cc_status.flags = CC_REVERSED;
cc_status.value2
= gen_rtx_MINUS (GET_MODE (SET_SRC (exp)),
XEXP (SET_SRC (exp), 0),
XEXP (SET_SRC (exp), 1));
}
return;
}
else if (REG_P (SET_DEST (exp))
|| (GET_CODE (SET_DEST (exp)) == STRICT_LOW_PART
&& REG_P (XEXP (SET_DEST (exp), 0))))
{
if (GET_MODE_SIZE (GET_MODE (SET_DEST (exp))) > UNITS_PER_WORD
|| GET_MODE_CLASS (GET_MODE (SET_DEST (exp))) == MODE_FLOAT)
{
if (GET_MODE (SET_DEST (exp)) == DImode
&& (GET_CODE (SET_SRC (exp)) == PLUS
|| GET_CODE (SET_SRC (exp)) == MINUS))
{
cc_status.flags = 0;
cc_status.value1 = SET_DEST (exp);
cc_status.value2 = SET_SRC (exp);
if (cris_reg_overlap_mentioned_p (cc_status.value1,
cc_status.value2))
cc_status.value2 = 0;
cc_status.flags |= CC_NO_OVERFLOW;
return;
}
}
else if (SET_SRC (exp) == const0_rtx)
{
if (cc_status.value1
&& modified_in_p (cc_status.value1, insn))
cc_status.value1 = 0;
if (cc_status.value2
&& modified_in_p (cc_status.value2, insn))
cc_status.value2 = 0;
return;
}
else
{
cc_status.flags = 0;
cc_status.value1 = SET_DEST (exp);
cc_status.value2 = SET_SRC (exp);
if (cris_reg_overlap_mentioned_p (cc_status.value1,
cc_status.value2))
cc_status.value2 = 0;
if (GET_CODE (SET_SRC (exp)) == PLUS
|| GET_CODE (SET_SRC (exp)) == MINUS
|| GET_CODE (SET_SRC (exp)) == NEG)
cc_status.flags |= CC_NO_OVERFLOW;
return;
}
}
else if (GET_CODE (SET_DEST (exp)) == MEM
|| (GET_CODE (SET_DEST (exp)) == STRICT_LOW_PART
&& GET_CODE (XEXP (SET_DEST (exp), 0)) == MEM))
{
if (cc_status.value1
&& modified_in_p (cc_status.value1, insn))
cc_status.value1 = 0;
if (cc_status.value2
&& modified_in_p (cc_status.value2, insn))
cc_status.value2 = 0;
return;
}
}
else if (GET_CODE (exp) == PARALLEL)
{
if (GET_CODE (XVECEXP (exp, 0, 0)) == SET
&& GET_CODE (XVECEXP (exp, 0, 1)) == SET
&& REG_P (XEXP (XVECEXP (exp, 0, 1), 0)))
{
if (REG_P (XEXP (XVECEXP (exp, 0, 0), 0))
&& GET_CODE (XEXP (XVECEXP (exp, 0, 0), 1)) == MEM)
{
cc_status.value1 = XEXP (XVECEXP (exp, 0, 0), 0);
cc_status.value2
= replace_equiv_address (XEXP (XVECEXP (exp, 0, 0), 1),
XEXP (XVECEXP (exp, 0, 1), 0));
cc_status.flags = 0;
if (cris_reg_overlap_mentioned_p (cc_status.value1,
cc_status.value2))
internal_error ("internal error: sideeffect-insn affecting main effect");
return;
}
else if ((REG_P (XEXP (XVECEXP (exp, 0, 0), 1))
|| XEXP (XVECEXP (exp, 0, 0), 1) == const0_rtx)
&& GET_CODE (XEXP (XVECEXP (exp, 0, 0), 0)) == MEM)
{
if (cc_status.value1
&& modified_in_p (cc_status.value1, insn))
cc_status.value1 = 0;
if (cc_status.value2
&& modified_in_p (cc_status.value2, insn))
cc_status.value2 = 0;
return;
}
}
}
break;
default:
abort ();
}
CC_STATUS_INIT;
}
int
cris_simple_epilogue (void)
{
int regno;
int reglimit = STACK_POINTER_REGNUM;
int lastreg = -1;
if (! reload_completed
|| frame_pointer_needed
|| get_frame_size () != 0
|| current_function_pretend_args_size
|| current_function_args_size
|| current_function_outgoing_args_size
|| current_function_calls_eh_return
|| !TARGET_PROLOGUE_EPILOGUE)
return 0;
for (regno = 0; regno < reglimit; regno++)
if ((regs_ever_live[regno] && ! call_used_regs[regno])
|| (regno == (int) PIC_OFFSET_TABLE_REGNUM
&& (current_function_uses_pic_offset_table
|| (flag_pic
&& regs_ever_live[regno + 1]
&& !call_used_regs[regno + 1]))))
{
if (lastreg != regno - 1)
return 0;
lastreg = regno;
}
return 1;
}
static bool
cris_rtx_costs (rtx x, int code, int outer_code, int *total)
{
switch (code)
{
case CONST_INT:
{
HOST_WIDE_INT val = INTVAL (x);
if (val == 0)
*total = 0;
else if (val < 32 && val >= -32)
*total = 1;
else if (val <= 32767 && val >= -32768)
*total = 2;
else
*total = 4;
return true;
}
case LABEL_REF:
*total = 6;
return true;
case CONST:
case SYMBOL_REF:
if (flag_pic)
{
if (cris_got_symbol (x))
*total = 2 + 4 + 6;
else
*total = 2 + 6;
}
else
*total = 6;
return true;
case CONST_DOUBLE:
if (x != CONST0_RTX (GET_MODE (x) == VOIDmode ? DImode : GET_MODE (x)))
*total = 12;
else
*total = 0;
return true;
case MULT:
if (GET_CODE (XEXP (x, 1)) != CONST_INT
|| exact_log2 (INTVAL (XEXP (x, 1)) < 0))
{
if (TARGET_HAS_MUL_INSNS)
{
*total = COSTS_N_INSNS (1) + COSTS_N_INSNS (1) / 2;
return true;
}
*total = COSTS_N_INSNS (132);
return true;
}
return false;
case UDIV:
case MOD:
case UMOD:
case DIV:
if (GET_CODE (XEXP (x, 1)) != CONST_INT
|| exact_log2 (INTVAL (XEXP (x, 1)) < 0))
{
*total = COSTS_N_INSNS (260);
return true;
}
return false;
case AND:
if (GET_CODE (XEXP (x, 1)) == CONST_INT
&& GET_CODE (XEXP (x, 0)) != CONST_INT
&& !CONST_OK_FOR_LETTER_P (INTVAL (XEXP (x, 1)), 'I'))
{
*total = (rtx_cost (XEXP (x, 0), outer_code) + 2
+ 2 * GET_MODE_NUNITS (GET_MODE (XEXP (x, 0))));
return true;
}
return false;
case ZERO_EXTEND: case SIGN_EXTEND:
*total = rtx_cost (XEXP (x, 0), outer_code);
return true;
default:
return false;
}
}
static int
cris_address_cost (rtx x)
{
if (BASE_OR_AUTOINCR_P (x))
return 0;
if (GET_CODE (x) == MEM)
return (2 + 4) / 2;
if (CONSTANT_P (x))
return flag_pic && cris_got_symbol (x) ? 2 * (2 + 4) / 2 : (2 + 4) / 2;
if (GET_CODE (x) == PLUS)
{
rtx tem1 = XEXP (x, 0);
rtx tem2 = XEXP (x, 1);
if ((GET_CODE (tem1) == MULT && BIAP_INDEX_P (tem1))
|| REG_P (tem1))
return 2 / 2;
if (GET_CODE (tem2) == CONST_INT
&& INTVAL (tem2) < 128 && INTVAL (tem2) >= -128)
return 2 / 2;
if (GET_CODE (tem2) == CONST_INT
&& CONST_OK_FOR_LETTER_P (INTVAL (tem2), 'L'))
return (2 + 2) / 2;
if (CONSTANT_P (tem2))
return (2 + 2 + 2) / 2;
return (2 + 2 + 2) / 2;
}
return 10;
}
int
cris_side_effect_mode_ok (enum rtx_code code, rtx *ops,
int lreg, int rreg, int rval,
int multop, int other_op)
{
int mult = multop < 0 ? 1 : INTVAL (ops[multop]);
rtx reg_rtx = ops[rreg];
rtx val_rtx = ops[rval];
if (! BASE_P (reg_rtx))
reg_rtx = val_rtx, val_rtx = ops[rreg];
if (! BASE_P (reg_rtx))
return 0;
if (!TARGET_SIDE_EFFECT_PREFIXES)
return 0;
if (GET_CODE (val_rtx) == MULT)
{
mult = INTVAL (XEXP (val_rtx, 1));
val_rtx = XEXP (val_rtx, 0);
code = MULT;
}
if (other_op >= 0)
{
if (GET_MODE_SIZE (GET_MODE (ops[other_op])) > UNITS_PER_WORD)
return 0;
if ((BASE_P (ops[lreg])
&& BASE_P (ops[other_op])
&& REGNO (ops[lreg]) == REGNO (ops[other_op]))
|| rtx_equal_p (ops[other_op], ops[lreg]))
return 0;
}
if (ops[lreg] == frame_pointer_rtx || ops[rreg] == frame_pointer_rtx
|| ops[rval] == frame_pointer_rtx
|| (other_op >= 0 && ops[other_op] == frame_pointer_rtx))
return 0;
if (code == PLUS
&& ! BASE_P (val_rtx))
{
if (rtx_equal_p (ops[lreg], reg_rtx)
&& GET_CODE (val_rtx) == CONST_INT
&& (INTVAL (val_rtx) <= 63 && INTVAL (val_rtx) >= -63))
return 0;
if (CONSTANT_P (val_rtx))
return flag_pic == 0 || cris_symbol (val_rtx) == 0;
if (GET_CODE (val_rtx) == MEM
&& BASE_OR_AUTOINCR_P (XEXP (val_rtx, 0)))
return 1;
if (GET_CODE (val_rtx) == SIGN_EXTEND
&& GET_CODE (XEXP (val_rtx, 0)) == MEM
&& BASE_OR_AUTOINCR_P (XEXP (XEXP (val_rtx, 0), 0)))
return 1;
return 0;
}
else if (code == MULT
|| (code == PLUS && BASE_P (val_rtx)))
{
if (rtx_equal_p (ops[lreg], reg_rtx)
|| (mult == 1 && rtx_equal_p (ops[lreg], val_rtx)))
return 0;
if (mult != 1 && mult != 2 && mult != 4)
return 0;
if (! BASE_P (reg_rtx))
return 0;
return 1;
}
internal_error ("internal error: cris_side_effect_mode_ok with bad operands");
}
static int
cris_reg_overlap_mentioned_p (rtx x, rtx in)
{
if (GET_CODE (in) == STRICT_LOW_PART)
in = XEXP (in, 0);
return reg_overlap_mentioned_p (x, in);
}
void
cris_target_asm_named_section (const char *name, unsigned int flags,
tree decl)
{
if (! TARGET_ELF)
default_no_named_section (name, flags, decl);
else
default_elf_asm_named_section (name, flags, decl);
}
int
cris_legitimate_pic_operand (rtx x)
{
return ! cris_symbol (x) || cris_got_symbol (x);
}
int
cris_symbol (rtx x)
{
switch (GET_CODE (x))
{
case SYMBOL_REF:
case LABEL_REF:
return 1;
case UNSPEC:
ASSERT_PLT_UNSPEC (x);
return 1;
case CONST:
return cris_symbol (XEXP (x, 0));
case PLUS:
case MINUS:
return cris_symbol (XEXP (x, 0)) || cris_symbol (XEXP (x, 1));
case CONST_INT:
case CONST_DOUBLE:
return 0;
default:
fatal_insn ("unrecognized supposed constant", x);
}
return 1;
}
int
cris_gotless_symbol (rtx x)
{
#ifdef ENABLE_CHECKING
if (!flag_pic)
abort ();
#endif
switch (GET_CODE (x))
{
case UNSPEC:
ASSERT_PLT_UNSPEC (x);
return 1;
case SYMBOL_REF:
if (cfun != NULL)
current_function_uses_pic_offset_table = 1;
return SYMBOL_REF_LOCAL_P (x);
case LABEL_REF:
return 1;
case CONST:
return cris_gotless_symbol (XEXP (x, 0));
case PLUS:
case MINUS:
{
int x0 = cris_gotless_symbol (XEXP (x, 0)) != 0;
int x1 = cris_gotless_symbol (XEXP (x, 1)) != 0;
return
(x0 ^ x1)
&& ! (x0 == 0 && cris_symbol (XEXP (x, 0)))
&& ! (x1 == 0 && cris_symbol (XEXP (x, 1)));
}
case CONST_INT:
case CONST_DOUBLE:
return 0;
default:
fatal_insn ("unrecognized supposed constant", x);
}
return 1;
}
int
cris_got_symbol (rtx x)
{
#ifdef ENABLE_CHECKING
if (!flag_pic)
abort ();
#endif
switch (GET_CODE (x))
{
case UNSPEC:
ASSERT_PLT_UNSPEC (x);
return 0;
case SYMBOL_REF:
if (cfun != NULL)
current_function_uses_pic_offset_table = 1;
return ! SYMBOL_REF_LOCAL_P (x);
case CONST:
return cris_got_symbol (XEXP (x, 0));
case LABEL_REF:
case PLUS:
case MINUS:
return 0;
case CONST_INT:
case CONST_DOUBLE:
return 0;
default:
fatal_insn ("unrecognized supposed constant in cris_global_pic_symbol",
x);
}
return 1;
}
void
cris_override_options (void)
{
if (cris_max_stackframe_str)
{
cris_max_stackframe = atoi (cris_max_stackframe_str);
if (cris_max_stackframe < 0 || cris_max_stackframe > 0x20000000)
internal_error ("-max-stackframe=%d is not usable, not between 0 and %d",
cris_max_stackframe, 0x20000000);
}
if (TARGET_SVINTO && cris_cpu_version < CRIS_CPU_SVINTO)
cris_cpu_version = CRIS_CPU_SVINTO;
else if (TARGET_ETRAX4_ADD && cris_cpu_version < CRIS_CPU_ETRAX4)
cris_cpu_version = CRIS_CPU_ETRAX4;
if (cris_cpu_str)
{
cris_cpu_version
= (*cris_cpu_str == 'v' ? atoi (cris_cpu_str + 1) : -1);
if (strcmp ("etrax4", cris_cpu_str) == 0)
cris_cpu_version = 3;
if (strcmp ("svinto", cris_cpu_str) == 0
|| strcmp ("etrax100", cris_cpu_str) == 0)
cris_cpu_version = 8;
if (strcmp ("ng", cris_cpu_str) == 0
|| strcmp ("etrax100lx", cris_cpu_str) == 0)
cris_cpu_version = 10;
if (cris_cpu_version < 0 || cris_cpu_version > 10)
error ("unknown CRIS version specification in -march= or -mcpu= : %s",
cris_cpu_str);
if (cris_cpu_version >= CRIS_CPU_ETRAX4)
target_flags |= TARGET_MASK_ETRAX4_ADD;
if (cris_cpu_version >= CRIS_CPU_SVINTO)
target_flags
|= (TARGET_MASK_SVINTO | TARGET_MASK_ALIGN_BY_32
| TARGET_MASK_STACK_ALIGN | TARGET_MASK_CONST_ALIGN
| TARGET_MASK_DATA_ALIGN);
}
if (cris_tune_str)
{
int cris_tune
= (*cris_tune_str == 'v' ? atoi (cris_tune_str + 1) : -1);
if (strcmp ("etrax4", cris_tune_str) == 0)
cris_tune = 3;
if (strcmp ("svinto", cris_tune_str) == 0
|| strcmp ("etrax100", cris_tune_str) == 0)
cris_tune = 8;
if (strcmp ("ng", cris_tune_str) == 0
|| strcmp ("etrax100lx", cris_tune_str) == 0)
cris_tune = 10;
if (cris_tune < 0 || cris_tune > 10)
error ("unknown CRIS cpu version specification in -mtune= : %s",
cris_tune_str);
if (cris_tune >= CRIS_CPU_SVINTO)
target_flags
|= (TARGET_MASK_STACK_ALIGN | TARGET_MASK_CONST_ALIGN
| TARGET_MASK_DATA_ALIGN | TARGET_MASK_ALIGN_BY_32);
}
if (flag_pic)
{
if (! TARGET_LINUX)
{
error ("-fPIC and -fpic are not supported in this configuration");
flag_pic = 0;
}
flag_no_function_cse = 1;
}
if (write_symbols == DWARF2_DEBUG && ! TARGET_ELF)
{
warning ("that particular -g option is invalid with -maout and -melinux");
write_symbols = DBX_DEBUG;
}
init_machine_status = cris_init_machine_status;
}
static void
cris_asm_output_mi_thunk (FILE *stream,
tree thunkdecl ATTRIBUTE_UNUSED,
HOST_WIDE_INT delta,
HOST_WIDE_INT vcall_offset ATTRIBUTE_UNUSED,
tree funcdecl)
{
if (delta > 0)
fprintf (stream, "\tadd%s " HOST_WIDE_INT_PRINT_DEC ",$%s\n",
ADDITIVE_SIZE_MODIFIER (delta), delta,
reg_names[CRIS_FIRST_ARG_REG]);
else if (delta < 0)
fprintf (stream, "\tsub%s " HOST_WIDE_INT_PRINT_DEC ",$%s\n",
ADDITIVE_SIZE_MODIFIER (-delta), -delta,
reg_names[CRIS_FIRST_ARG_REG]);
if (flag_pic)
{
const char *name = XSTR (XEXP (DECL_RTL (funcdecl), 0), 0);
name = (* targetm.strip_name_encoding) (name);
fprintf (stream, "add.d ");
assemble_name (stream, name);
fprintf (stream, "%s,$pc\n", CRIS_PLT_PCOFFSET_SUFFIX);
}
else
{
fprintf (stream, "jump ");
assemble_name (stream, XSTR (XEXP (DECL_RTL (funcdecl), 0), 0));
fprintf (stream, "\n");
}
}
static void
cris_file_start (void)
{
targetm.file_start_app_off = !(TARGET_PDEBUG || flag_print_asm_name);
targetm.file_start_file_directive = TARGET_ELF;
default_file_start ();
}
static void
cris_init_libfuncs (void)
{
set_optab_libfunc (smul_optab, SImode, "__Mul");
set_optab_libfunc (sdiv_optab, SImode, "__Div");
set_optab_libfunc (udiv_optab, SImode, "__Udiv");
set_optab_libfunc (smod_optab, SImode, "__Mod");
set_optab_libfunc (umod_optab, SImode, "__Umod");
}
void
cris_init_expanders (void)
{
}
static struct machine_function *
cris_init_machine_status (void)
{
return ggc_alloc_cleared (sizeof (struct machine_function));
}
rtx
cris_split_movdx (rtx *operands)
{
enum machine_mode mode = GET_MODE (operands[0]);
rtx dest = operands[0];
rtx src = operands[1];
rtx val;
if (GET_CODE (dest) == SUBREG || GET_CODE (src) == SUBREG)
abort ();
start_sequence ();
if (GET_CODE (dest) == REG)
{
int dregno = REGNO (dest);
if (GET_CODE (src) == REG)
{
int sregno = REGNO (src);
int reverse = (dregno == sregno + 1);
emit_insn (gen_rtx_SET (VOIDmode,
operand_subword (dest, reverse, TRUE, mode),
operand_subword (src, reverse, TRUE, mode)));
emit_insn (gen_rtx_SET (VOIDmode,
operand_subword (dest, !reverse, TRUE, mode),
operand_subword (src, !reverse, TRUE, mode)));
}
else if (GET_CODE (src) == CONST_INT || GET_CODE (src) == CONST_DOUBLE)
{
rtx words[2];
split_double (src, &words[0], &words[1]);
emit_insn (gen_rtx_SET (VOIDmode,
operand_subword (dest, 0, TRUE, mode),
words[0]));
emit_insn (gen_rtx_SET (VOIDmode,
operand_subword (dest, 1, TRUE, mode),
words[1]));
}
else if (GET_CODE (src) == MEM)
{
rtx addr = XEXP (src, 0);
int reverse
= (refers_to_regno_p (dregno, dregno + 1, addr, NULL) != 0);
if (GET_CODE (addr) == POST_INC)
{
emit_insn (gen_rtx_SET (VOIDmode,
operand_subword (dest, 0, TRUE, mode),
change_address (src, SImode, addr)));
emit_insn (gen_rtx_SET (VOIDmode,
operand_subword (dest, 1, TRUE, mode),
change_address (src, SImode, addr)));
}
else
{
if (side_effects_p (addr))
fatal_insn ("unexpected side-effects in address", addr);
emit_insn (gen_rtx_SET
(VOIDmode,
operand_subword (dest, reverse, TRUE, mode),
change_address
(src, SImode,
plus_constant (addr,
reverse * UNITS_PER_WORD))));
emit_insn (gen_rtx_SET
(VOIDmode,
operand_subword (dest, ! reverse, TRUE, mode),
change_address
(src, SImode,
plus_constant (addr,
(! reverse) *
UNITS_PER_WORD))));
}
}
else
abort ();
}
else if (GET_CODE (dest) == MEM
&& (GET_CODE (src) == REG
|| src == const0_rtx
|| src == CONST0_RTX (DFmode)))
{
rtx addr = XEXP (dest, 0);
if (GET_CODE (addr) == POST_INC)
{
emit_insn (gen_rtx_SET (VOIDmode,
change_address (dest, SImode, addr),
operand_subword (src, 0, TRUE, mode)));
emit_insn (gen_rtx_SET (VOIDmode,
change_address (dest, SImode, addr),
operand_subword (src, 1, TRUE, mode)));
}
else
{
if (side_effects_p (addr))
fatal_insn ("unexpected side-effects in address", addr);
emit_insn (gen_rtx_SET
(VOIDmode,
change_address (dest, SImode, addr),
operand_subword (src, 0, TRUE, mode)));
emit_insn (gen_rtx_SET
(VOIDmode,
change_address (dest, SImode,
plus_constant (addr,
UNITS_PER_WORD)),
operand_subword (src, 1, TRUE, mode)));
}
}
else
abort ();
val = get_insns ();
end_sequence ();
return val;
}
static void
cris_output_addr_const (FILE *file, rtx x)
{
in_code++;
output_addr_const (file, x);
in_code--;
}
void
cris_asm_output_symbol_ref (FILE *file, rtx x)
{
if (flag_pic && in_code > 0)
{
const char *origstr = XSTR (x, 0);
const char *str;
str = (* targetm.strip_name_encoding) (origstr);
if (cris_gotless_symbol (x))
{
if (! cris_pic_sympart_only)
fprintf (file, "$%s+", reg_names [PIC_OFFSET_TABLE_REGNUM]);
assemble_name (file, str);
fprintf (file, ":GOTOFF");
}
else if (cris_got_symbol (x))
{
if (cris_pic_sympart_only)
abort ();
fprintf (file, "[$%s+", reg_names [PIC_OFFSET_TABLE_REGNUM]);
assemble_name (file, XSTR (x, 0));
if (flag_pic == 1)
fprintf (file, ":GOT16]");
else
fprintf (file, ":GOT]");
}
else
LOSE_AND_RETURN ("unexpected PIC symbol", x);
if (! current_function_uses_pic_offset_table)
output_operand_lossage ("PIC register isn't set up");
}
else
assemble_name (file, XSTR (x, 0));
}
void
cris_asm_output_label_ref (FILE *file, char *buf)
{
if (flag_pic && in_code > 0)
{
if (! cris_pic_sympart_only)
fprintf (file, "$%s+", reg_names [PIC_OFFSET_TABLE_REGNUM]);
assemble_name (file, buf);
fprintf (file, ":GOTOFF");
if (! current_function_uses_pic_offset_table)
internal_error ("emitting PIC operand, but PIC register isn't set up");
}
else
assemble_name (file, buf);
}
bool
cris_output_addr_const_extra (FILE *file, rtx x)
{
switch (GET_CODE (x))
{
const char *origstr;
const char *str;
case UNSPEC:
ASSERT_PLT_UNSPEC (x);
x = XVECEXP (x, 0, 0);
origstr = XSTR (x, 0);
str = (* targetm.strip_name_encoding) (origstr);
if (cris_pic_sympart_only)
{
assemble_name (file, str);
fprintf (file, ":PLTG");
}
else
{
if (TARGET_AVOID_GOTPLT)
abort ();
fprintf (file, "[$%s+", reg_names [PIC_OFFSET_TABLE_REGNUM]);
assemble_name (file, XSTR (x, 0));
if (flag_pic == 1)
fprintf (file, ":GOTPLT16]");
else
fprintf (file, ":GOTPLT]");
}
return true;
default:
return false;
}
}
static rtx
cris_struct_value_rtx (tree fntype ATTRIBUTE_UNUSED,
int incoming ATTRIBUTE_UNUSED)
{
return gen_rtx_REG (Pmode, CRIS_STRUCT_VALUE_REGNUM);
}
static void
cris_setup_incoming_varargs (CUMULATIVE_ARGS *ca,
enum machine_mode mode ATTRIBUTE_UNUSED,
tree type ATTRIBUTE_UNUSED,
int *pretend_arg_size,
int second_time)
{
if (ca->regs < CRIS_MAX_ARGS_IN_REGS)
*pretend_arg_size = (CRIS_MAX_ARGS_IN_REGS - ca->regs) * 4;
if (TARGET_PDEBUG)
{
fprintf (asm_out_file,
"\n; VA:: ANSI: %d args before, anon @ #%d, %dtime\n",
ca->regs, *pretend_arg_size, second_time);
}
}
static bool
cris_pass_by_reference (CUMULATIVE_ARGS *ca ATTRIBUTE_UNUSED,
enum machine_mode mode, tree type,
bool named ATTRIBUTE_UNUSED)
{
return (targetm.calls.must_pass_in_stack (mode, type)
|| CRIS_FUNCTION_ARG_SIZE (mode, type) > 8);
}
static int
cris_arg_partial_bytes (CUMULATIVE_ARGS *ca, enum machine_mode mode,
tree type, bool named ATTRIBUTE_UNUSED)
{
if (ca->regs == CRIS_MAX_ARGS_IN_REGS - 1
&& !targetm.calls.must_pass_in_stack (mode, type)
&& CRIS_FUNCTION_ARG_SIZE (mode, type) > 4
&& CRIS_FUNCTION_ARG_SIZE (mode, type) <= 8)
return UNITS_PER_WORD;
else
return 0;
}
#if 0
enum rtx_code Get_code (rtx);
enum rtx_code
Get_code (rtx x)
{
return GET_CODE (x);
}
const char *Get_mode (rtx);
const char *
Get_mode (rtx x)
{
return GET_MODE_NAME (GET_MODE (x));
}
rtx Xexp (rtx, int);
rtx
Xexp (rtx x, int n)
{
return XEXP (x, n);
}
rtx Xvecexp (rtx, int, int);
rtx
Xvecexp (rtx x, int n, int m)
{
return XVECEXP (x, n, m);
}
int Get_rtx_len (rtx);
int
Get_rtx_len (rtx x)
{
return GET_RTX_LENGTH (GET_CODE (x));
}
rtx Next_insn (rtx);
rtx
Next_insn (rtx insn)
{
return NEXT_INSN (insn);
}
rtx Prev_insn (rtx);
rtx
Prev_insn (rtx insn)
{
return PREV_INSN (insn);
}
#endif
#include "gt-cris.h"