#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 "reload.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) \
CRIS_ASSERT (XINT (x, 1) == CRIS_UNSPEC_PLT \
&& ((GET_CODE (XVECEXP (x, 0, 0)) == SYMBOL_REF) \
|| GET_CODE (XVECEXP (x, 0, 0)) == LABEL_REF))
#define LOSE_AND_RETURN(msgid, x) \
do \
{ \
cris_operand_lossage (msgid, x); \
return; \
} while (0)
enum cris_retinsn_type
{ CRIS_RETINSN_UNKNOWN = 0, CRIS_RETINSN_RET, CRIS_RETINSN_JUMP };
struct machine_function GTY(())
{
int needs_return_address_on_stack;
int stdarg_regs;
enum cris_retinsn_type return_type;
};
static char cris_output_insn_is_bound = 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_operand_lossage (const char *, rtx);
static int cris_reg_saved_in_regsave_area (unsigned int, bool);
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 tree cris_md_asm_clobbers (tree, tree, tree);
static bool cris_handle_option (size_t, const char *, int);
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_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
#undef TARGET_MD_ASM_CLOBBERS
#define TARGET_MD_ASM_CLOBBERS cris_md_asm_clobbers
#undef TARGET_DEFAULT_TARGET_FLAGS
#define TARGET_DEFAULT_TARGET_FLAGS (TARGET_DEFAULT | CRIS_SUBTARGET_DEFAULT)
#undef TARGET_HANDLE_OPTION
#define TARGET_HANDLE_OPTION cris_handle_option
struct gcc_target targetm = TARGET_INITIALIZER;
bool
cris_movem_load_rest_p (rtx op, int offs)
{
unsigned int reg_count = XVECLEN (op, 0) - offs;
rtx src_addr;
int i;
rtx elt;
int setno;
int regno_dir = 1;
unsigned int regno = 0;
if (reg_count <= 1
|| GET_CODE (XVECEXP (op, 0, offs)) != SET
|| GET_CODE (SET_DEST (XVECEXP (op, 0, offs))) != REG
|| GET_CODE (SET_SRC (XVECEXP (op, 0, offs))) != MEM)
return false;
if (GET_CODE (SET_SRC (XVECEXP (op, 0, offs + 1))) == PLUS)
{
rtx reg = XEXP (SET_SRC (XVECEXP (op, 0, offs + 1)), 0);
rtx inc = XEXP (SET_SRC (XVECEXP (op, 0, offs + 1)), 1);
reg_count--;
if (reg_count == 1
|| !REG_P (reg)
|| !REG_P (SET_DEST (XVECEXP (op, 0, offs + 1)))
|| REGNO (reg) != REGNO (SET_DEST (XVECEXP (op, 0, offs + 1)))
|| GET_CODE (inc) != CONST_INT
|| INTVAL (inc) != (HOST_WIDE_INT) reg_count * 4)
return false;
i = offs + 2;
}
else
i = offs + 1;
regno_dir = -1;
regno = reg_count - 1;
elt = XVECEXP (op, 0, offs);
src_addr = XEXP (SET_SRC (elt), 0);
if (GET_CODE (elt) != SET
|| GET_CODE (SET_DEST (elt)) != REG
|| GET_MODE (SET_DEST (elt)) != SImode
|| REGNO (SET_DEST (elt)) != regno
|| GET_CODE (SET_SRC (elt)) != MEM
|| GET_MODE (SET_SRC (elt)) != SImode
|| !memory_address_p (SImode, src_addr))
return false;
for (setno = 1; i < XVECLEN (op, 0); setno++, i++)
{
rtx elt = XVECEXP (op, 0, i);
regno += regno_dir;
if (GET_CODE (elt) != SET
|| GET_CODE (SET_DEST (elt)) != REG
|| GET_MODE (SET_DEST (elt)) != SImode
|| REGNO (SET_DEST (elt)) != regno
|| GET_CODE (SET_SRC (elt)) != MEM
|| GET_MODE (SET_SRC (elt)) != SImode
|| GET_CODE (XEXP (SET_SRC (elt), 0)) != PLUS
|| ! rtx_equal_p (XEXP (XEXP (SET_SRC (elt), 0), 0), src_addr)
|| GET_CODE (XEXP (XEXP (SET_SRC (elt), 0), 1)) != CONST_INT
|| INTVAL (XEXP (XEXP (SET_SRC (elt), 0), 1)) != setno * 4)
return false;
}
return true;
}
bool
cris_store_multiple_op_p (rtx op)
{
int reg_count = XVECLEN (op, 0);
rtx dest;
rtx dest_addr;
rtx dest_base;
int i;
rtx elt;
int setno;
int regno_dir = 1;
int regno = 0;
int offset = 0;
if (reg_count <= 1)
return false;
elt = XVECEXP (op, 0, 0);
if (GET_CODE (elt) != SET)
return false;
dest = SET_DEST (elt);
if (GET_CODE (SET_SRC (elt)) != REG
|| GET_CODE (dest) != MEM)
return false;
dest_addr = XEXP (dest, 0);
if (GET_CODE (SET_SRC (XVECEXP (op, 0, 1))) == PLUS)
{
rtx reg = XEXP (SET_SRC (XVECEXP (op, 0, 1)), 0);
rtx inc = XEXP (SET_SRC (XVECEXP (op, 0, 1)), 1);
reg_count--;
if (reg_count == 1
|| !REG_P (reg)
|| !REG_P (SET_DEST (XVECEXP (op, 0, 1)))
|| REGNO (reg) != REGNO (SET_DEST (XVECEXP (op, 0, 1)))
|| GET_CODE (inc) != CONST_INT
|| !((REG_P (dest_addr)
&& REGNO (dest_addr) == REGNO (reg)
&& INTVAL (inc) == (HOST_WIDE_INT) reg_count * 4)
|| (GET_CODE (dest_addr) == PLUS
&& REG_P (XEXP (dest_addr, 0))
&& REGNO (XEXP (dest_addr, 0)) == REGNO (reg)
&& GET_CODE (XEXP (dest_addr, 1)) == CONST_INT
&& INTVAL (XEXP (dest_addr, 1)) == INTVAL (inc))))
return false;
i = 2;
}
else
i = 1;
regno_dir = -1;
regno = reg_count - 1;
if (GET_CODE (elt) != SET
|| GET_CODE (SET_SRC (elt)) != REG
|| GET_MODE (SET_SRC (elt)) != SImode
|| REGNO (SET_SRC (elt)) != (unsigned int) regno
|| GET_CODE (SET_DEST (elt)) != MEM
|| GET_MODE (SET_DEST (elt)) != SImode)
return false;
if (REG_P (dest_addr))
{
dest_base = dest_addr;
offset = 0;
}
else if (GET_CODE (dest_addr) == PLUS
&& REG_P (XEXP (dest_addr, 0))
&& GET_CODE (XEXP (dest_addr, 1)) == CONST_INT)
{
dest_base = XEXP (dest_addr, 0);
offset = INTVAL (XEXP (dest_addr, 1));
}
else
return false;
for (setno = 1; i < XVECLEN (op, 0); setno++, i++)
{
rtx elt = XVECEXP (op, 0, i);
regno += regno_dir;
if (GET_CODE (elt) != SET
|| GET_CODE (SET_SRC (elt)) != REG
|| GET_MODE (SET_SRC (elt)) != SImode
|| REGNO (SET_SRC (elt)) != (unsigned int) regno
|| GET_CODE (SET_DEST (elt)) != MEM
|| GET_MODE (SET_DEST (elt)) != SImode
|| GET_CODE (XEXP (SET_DEST (elt), 0)) != PLUS
|| ! rtx_equal_p (XEXP (XEXP (SET_DEST (elt), 0), 0), dest_base)
|| GET_CODE (XEXP (XEXP (SET_DEST (elt), 0), 1)) != CONST_INT
|| INTVAL (XEXP (XEXP (SET_DEST (elt), 0), 1)) != setno * 4 + offset)
return false;
}
return true;
}
void
cris_conditional_register_usage (void)
{
if (flag_pic)
fixed_regs[PIC_OFFSET_TABLE_REGNUM]
= call_used_regs[PIC_OFFSET_TABLE_REGNUM] = 1;
if (TARGET_HAS_MUL_INSNS)
fixed_regs[CRIS_MOF_REGNUM] = 0;
if (cris_cpu_version < 8)
reg_names[CRIS_CC0_REGNUM] = "ccr";
}
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:
internal_error ("MULT case in cris_op_str");
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 int
cris_reg_saved_in_regsave_area (unsigned int regno, bool got_really_used)
{
return
(((regs_ever_live[regno]
&& !call_used_regs[regno])
|| (regno == PIC_OFFSET_TABLE_REGNUM
&& (got_really_used
|| (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)));
}
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;
}
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 'o':
{
int regno;
if (GET_CODE (x) != PARALLEL)
LOSE_AND_RETURN ("invalid operand for 'o' modifier", x);
regno
= (GET_CODE (SET_SRC (XVECEXP (x, 0, 1))) == PLUS
? XVECLEN (x, 0) - 2
: XVECLEN (x, 0) - 1);
fprintf (file, "$%s", reg_names [regno]);
}
return;
case 'O':
{
rtx addr;
if (GET_CODE (x) != PARALLEL)
LOSE_AND_RETURN ("invalid operand for 'O' modifier", x);
addr = GET_CODE (SET_SRC (XVECEXP (x, 0, 0))) == MEM
? XEXP (SET_SRC (XVECEXP (x, 0, 0)), 0)
: XEXP (SET_DEST (XVECEXP (x, 0, 0)), 0);
if (GET_CODE (SET_SRC (XVECEXP (x, 0, 1))) == PLUS)
{
if (REG_P (addr))
addr = gen_rtx_POST_INC (SImode, addr);
else
{
fprintf (file, "[$%s=$%s%s%d]",
reg_names [REGNO (SET_DEST (XVECEXP (x, 0, 1)))],
reg_names [REGNO (XEXP (addr, 0))],
INTVAL (XEXP (addr, 1)) < 0 ? "" : "+",
(int) INTVAL (XEXP (addr, 1)));
return;
}
}
output_address (addr);
}
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 ':':
if (! flag_pic)
internal_error ("invalid use of ':' modifier");
fprintf (file, "$%s", reg_names [PIC_OFFSET_TABLE_REGNUM]);
return;
case 'H':
switch (GET_CODE (operand))
{
case CONST_INT:
fprintf (file, HOST_WIDE_INT_PRINT_DEC,
INTVAL (operand_subword (operand, 1, 0, DImode)));
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':
cris_output_insn_is_bound = 0;
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 (flag_pic == 1 && CONSTANT_P (operand))
{
int flag_pic_save = flag_pic;
flag_pic = 2;
cris_output_addr_const (file, operand);
flag_pic = flag_pic_save;
return;
}
break;
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
&& REGNO (operand) != CRIS_MOF_REGNUM
&& REGNO (operand) != CRIS_SRP_REGNUM
&& REGNO (operand) != CRIS_CC0_REGNUM)
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:
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;
}
bool
cris_return_address_on_stack (void)
{
return regs_ever_live[CRIS_SRP_REGNUM]
|| cfun->machine->needs_return_address_on_stack;
}
bool
cris_return_address_on_stack_for_return (void)
{
return cfun->machine->return_type == CRIS_RETINSN_RET ? false
: cris_return_address_on_stack ();
}
static int
cris_initial_frame_pointer_offset (void)
{
int regno;
int offs = 0;
bool got_really_used = false;
if (current_function_uses_pic_offset_table)
{
push_topmost_sequence ();
got_really_used
= reg_used_between_p (pic_offset_table_rtx, get_insns (),
NULL_RTX);
pop_topmost_sequence ();
}
for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
if (cris_reg_saved_in_regsave_area (regno, got_really_used))
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 ();
bool return_address_on_stack = cris_return_address_on_stack ();
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;
gcc_unreachable ();
}
bool
cris_reload_address_legitimized (rtx x,
enum machine_mode mode ATTRIBUTE_UNUSED,
int opnum ATTRIBUTE_UNUSED,
int itype,
int ind_levels ATTRIBUTE_UNUSED)
{
enum reload_type type = itype;
rtx op0, op1;
rtx *op0p;
rtx *op1p;
if (GET_CODE (x) != PLUS)
return false;
op0 = XEXP (x, 0);
op0p = &XEXP (x, 0);
op1 = XEXP (x, 1);
op1p = &XEXP (x, 1);
if (!REG_P (op1))
return false;
if (GET_CODE (op0) == SIGN_EXTEND
&& GET_CODE (XEXP (op0, 0)) == MEM)
{
rtx op00 = XEXP (op0, 0);
rtx op000 = XEXP (op00, 0);
rtx *op000p = &XEXP (op00, 0);
if ((GET_MODE (op00) == HImode || GET_MODE (op00) == QImode)
&& (REG_P (op000)
|| (GET_CODE (op000) == POST_INC && REG_P (XEXP (op000, 0)))))
{
bool something_reloaded = false;
if (GET_CODE (op000) == POST_INC
&& REG_P (XEXP (op000, 0))
&& REGNO (XEXP (op000, 0)) > CRIS_LAST_GENERAL_REGISTER)
return false;
if ((REG_P (op000)
&& REGNO (op000) > CRIS_LAST_GENERAL_REGISTER))
{
push_reload (op000, NULL_RTX, op000p, NULL, GENERAL_REGS,
GET_MODE (x), VOIDmode, 0, 0, opnum, type);
something_reloaded = true;
}
if (REGNO (op1) > CRIS_LAST_GENERAL_REGISTER)
{
push_reload (op1, NULL_RTX, op1p, NULL, GENERAL_REGS,
GET_MODE (x), VOIDmode, 0, 0,
opnum, type);
something_reloaded = true;
}
gcc_assert (something_reloaded);
return true;
}
}
return false;
}
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:
internal_error ("unknown cc_attr value");
}
CC_STATUS_INIT;
}
bool
cris_simple_epilogue (void)
{
unsigned int regno;
unsigned int reglimit = STACK_POINTER_REGNUM;
bool got_really_used = false;
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 false;
if (current_function_uses_pic_offset_table)
{
push_topmost_sequence ();
got_really_used
= reg_used_between_p (pic_offset_table_rtx, get_insns (), NULL_RTX);
pop_topmost_sequence ();
}
for (regno = 0; regno < reglimit; regno++)
if (cris_reg_saved_in_regsave_area (regno, got_really_used))
return false;
return true;
}
void
cris_expand_return (bool on_stack)
{
emit_jump_insn (gen_rtx_RETURN (VOIDmode));
CRIS_ASSERT (cfun->machine->return_type != CRIS_RETINSN_RET || !on_stack);
CRIS_ASSERT (cfun->machine->return_type != CRIS_RETINSN_JUMP || on_stack);
cfun->machine->return_type
= on_stack ? CRIS_RETINSN_JUMP : CRIS_RETINSN_RET;
}
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:
*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 (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 1;
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);
}
bool
cris_valid_pic_const (rtx x)
{
gcc_assert (flag_pic);
switch (GET_CODE (x))
{
case CONST_INT:
case CONST_DOUBLE:
return true;
default:
;
}
if (GET_CODE (x) != CONST)
return false;
x = XEXP (x, 0);
if (GET_CODE (x) == PLUS
&& GET_CODE (XEXP (x, 0)) == UNSPEC
&& XINT (XEXP (x, 0), 1) == CRIS_UNSPEC_GOTREL
&& GET_CODE (XEXP (x, 1)) == CONST_INT)
x = XEXP (x, 0);
if (GET_CODE (x) == UNSPEC)
switch (XINT (x, 1))
{
case CRIS_UNSPEC_PLT:
case CRIS_UNSPEC_PLTGOTREAD:
case CRIS_UNSPEC_GOTREAD:
case CRIS_UNSPEC_GOTREL:
return true;
default:
gcc_unreachable ();
}
return cris_pic_symbol_type_of (x) == cris_no_symbol;
}
enum cris_pic_symbol_type
cris_pic_symbol_type_of (rtx x)
{
switch (GET_CODE (x))
{
case SYMBOL_REF:
return SYMBOL_REF_LOCAL_P (x)
? cris_gotrel_symbol : cris_got_symbol;
case LABEL_REF:
return cris_gotrel_symbol;
case CONST:
return cris_pic_symbol_type_of (XEXP (x, 0));
case PLUS:
case MINUS:
{
enum cris_pic_symbol_type t1 = cris_pic_symbol_type_of (XEXP (x, 0));
enum cris_pic_symbol_type t2 = cris_pic_symbol_type_of (XEXP (x, 1));
gcc_assert (t1 == cris_no_symbol || t2 == cris_no_symbol);
if (t1 == cris_got_symbol || t1 == cris_got_symbol)
return cris_got_symbol_needing_fixup;
return t1 != cris_no_symbol ? t1 : t2;
}
case CONST_INT:
case CONST_DOUBLE:
return cris_no_symbol;
case UNSPEC:
return cris_invalid_pic_symbol;
default:
fatal_insn ("unrecognized supposed constant", x);
}
gcc_unreachable ();
}
int
cris_legitimate_pic_operand (rtx x)
{
return cris_valid_pic_const (x);
}
static bool
cris_handle_option (size_t code, const char *arg ATTRIBUTE_UNUSED,
int value ATTRIBUTE_UNUSED)
{
switch (code)
{
case OPT_metrax100:
target_flags
|= (MASK_SVINTO
+ MASK_ETRAX4_ADD
+ MASK_ALIGN_BY_32);
break;
case OPT_mno_etrax100:
target_flags
&= ~(MASK_SVINTO
+ MASK_ETRAX4_ADD
+ MASK_ALIGN_BY_32);
break;
case OPT_m32_bit:
case OPT_m32bit:
target_flags
|= (MASK_STACK_ALIGN
+ MASK_CONST_ALIGN
+ MASK_DATA_ALIGN
+ MASK_ALIGN_BY_32);
break;
case OPT_m16_bit:
case OPT_m16bit:
target_flags
|= (MASK_STACK_ALIGN
+ MASK_CONST_ALIGN
+ MASK_DATA_ALIGN);
break;
case OPT_m8_bit:
case OPT_m8bit:
target_flags
&= ~(MASK_STACK_ALIGN
+ MASK_CONST_ALIGN
+ MASK_DATA_ALIGN);
break;
default:
break;
}
CRIS_SUBTARGET_HANDLE_OPTION(code, arg, value);
return true;
}
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 |= MASK_ETRAX4_ADD;
if (cris_cpu_version >= CRIS_CPU_SVINTO)
target_flags
|= (MASK_SVINTO | MASK_ALIGN_BY_32
| MASK_STACK_ALIGN | MASK_CONST_ALIGN
| 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
|= (MASK_STACK_ALIGN | MASK_CONST_ALIGN
| MASK_DATA_ALIGN | 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 (0, "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;
CRIS_ASSERT (GET_CODE (dest) != SUBREG && GET_CODE (src) != SUBREG);
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)
{
rtx mem;
rtx insn;
mem = change_address (src, SImode, addr);
insn
= gen_rtx_SET (VOIDmode,
operand_subword (dest, 0, TRUE, mode), mem);
insn = emit_insn (insn);
if (GET_CODE (XEXP (mem, 0)) == POST_INC)
REG_NOTES (insn)
= alloc_EXPR_LIST (REG_INC, XEXP (XEXP (mem, 0), 0),
REG_NOTES (insn));
mem = change_address (src, SImode, addr);
insn
= gen_rtx_SET (VOIDmode,
operand_subword (dest, 1, TRUE, mode), mem);
insn = emit_insn (insn);
if (GET_CODE (XEXP (mem, 0)) == POST_INC)
REG_NOTES (insn)
= alloc_EXPR_LIST (REG_INC, XEXP (XEXP (mem, 0), 0),
REG_NOTES (insn));
}
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
internal_error ("Unknown src");
}
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)
{
rtx mem;
rtx insn;
mem = change_address (dest, SImode, addr);
insn
= gen_rtx_SET (VOIDmode,
mem, operand_subword (src, 0, TRUE, mode));
insn = emit_insn (insn);
if (GET_CODE (XEXP (mem, 0)) == POST_INC)
REG_NOTES (insn)
= alloc_EXPR_LIST (REG_INC, XEXP (XEXP (mem, 0), 0),
REG_NOTES (insn));
mem = change_address (dest, SImode, addr);
insn
= gen_rtx_SET (VOIDmode,
mem,
operand_subword (src, 1, TRUE, mode));
insn = emit_insn (insn);
if (GET_CODE (XEXP (mem, 0)) == POST_INC)
REG_NOTES (insn)
= alloc_EXPR_LIST (REG_INC, XEXP (XEXP (mem, 0), 0),
REG_NOTES (insn));
}
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
internal_error ("Unknown dest");
val = get_insns ();
end_sequence ();
return val;
}
void
cris_expand_prologue (void)
{
int regno;
int size = get_frame_size ();
int cfoa_size = current_function_outgoing_args_size;
int last_movem_reg = -1;
int framesize = 0;
rtx mem, insn;
int return_address_on_stack = cris_return_address_on_stack ();
int got_really_used = false;
int n_movem_regs = 0;
int pretend = current_function_pretend_args_size;
if (!TARGET_PROLOGUE_EPILOGUE)
return;
CRIS_ASSERT (size >= 0);
if (current_function_uses_pic_offset_table)
{
push_topmost_sequence ();
got_really_used
= reg_used_between_p (pic_offset_table_rtx, get_insns (), NULL_RTX);
pop_topmost_sequence ();
}
if (TARGET_STACK_ALIGN)
size = TARGET_ALIGN_BY_32 ? (size + 3) & ~3 : (size + 1) & ~1;
if (pretend)
{
int stdarg_regs = cfun->machine->stdarg_regs;
framesize += pretend;
for (regno = CRIS_FIRST_ARG_REG + CRIS_MAX_ARGS_IN_REGS - 1;
stdarg_regs > 0;
regno--, pretend -= 4, stdarg_regs--)
{
insn = emit_insn (gen_rtx_SET (VOIDmode,
stack_pointer_rtx,
plus_constant (stack_pointer_rtx,
-4)));
RTX_FRAME_RELATED_P (insn) = 1;
mem = gen_rtx_MEM (SImode, stack_pointer_rtx);
set_mem_alias_set (mem, get_varargs_alias_set ());
insn = emit_move_insn (mem, gen_rtx_raw_REG (SImode, regno));
}
}
if (return_address_on_stack)
{
insn = emit_insn (gen_rtx_SET (VOIDmode,
stack_pointer_rtx,
plus_constant (stack_pointer_rtx,
-4 - pretend)));
pretend = 0;
RTX_FRAME_RELATED_P (insn) = 1;
mem = gen_rtx_MEM (SImode, stack_pointer_rtx);
set_mem_alias_set (mem, get_frame_alias_set ());
insn = emit_move_insn (mem, gen_rtx_raw_REG (SImode, CRIS_SRP_REGNUM));
RTX_FRAME_RELATED_P (insn) = 1;
framesize += 4;
}
if (frame_pointer_needed)
{
insn = emit_insn (gen_rtx_SET (VOIDmode,
stack_pointer_rtx,
plus_constant (stack_pointer_rtx,
-4 - pretend)));
pretend = 0;
RTX_FRAME_RELATED_P (insn) = 1;
mem = gen_rtx_MEM (SImode, stack_pointer_rtx);
set_mem_alias_set (mem, get_frame_alias_set ());
insn = emit_move_insn (mem, frame_pointer_rtx);
RTX_FRAME_RELATED_P (insn) = 1;
insn = emit_move_insn (frame_pointer_rtx, stack_pointer_rtx);
RTX_FRAME_RELATED_P (insn) = 1;
framesize += 4;
}
size += pretend;
for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
{
if (cris_reg_saved_in_regsave_area (regno, got_really_used))
{
n_movem_regs++;
if (regno == last_movem_reg + 1)
last_movem_reg = regno;
else
{
if (last_movem_reg != -1)
{
int n_saved
= (n_movem_regs == 1) ? 1 : 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 || n_saved == 1)
&& TARGET_SIDE_EFFECT_PREFIXES)
{
mem
= gen_rtx_MEM (SImode,
plus_constant (stack_pointer_rtx,
-(n_saved * 4 + size)));
set_mem_alias_set (mem, get_frame_alias_set ());
insn
= cris_emit_movem_store (mem, GEN_INT (n_saved),
-(n_saved * 4 + size),
true);
}
else
{
insn
= gen_rtx_SET (VOIDmode,
stack_pointer_rtx,
plus_constant (stack_pointer_rtx,
-(n_saved * 4 + size)));
insn = emit_insn (insn);
RTX_FRAME_RELATED_P (insn) = 1;
mem = gen_rtx_MEM (SImode, stack_pointer_rtx);
set_mem_alias_set (mem, get_frame_alias_set ());
insn = cris_emit_movem_store (mem, GEN_INT (n_saved),
0, true);
}
framesize += n_saved * 4 + size;
last_movem_reg = -1;
size = 0;
}
insn = emit_insn (gen_rtx_SET (VOIDmode,
stack_pointer_rtx,
plus_constant (stack_pointer_rtx,
-4 - size)));
RTX_FRAME_RELATED_P (insn) = 1;
mem = gen_rtx_MEM (SImode, stack_pointer_rtx);
set_mem_alias_set (mem, get_frame_alias_set ());
insn = emit_move_insn (mem, gen_rtx_raw_REG (SImode, regno));
RTX_FRAME_RELATED_P (insn) = 1;
framesize += 4 + size;
size = 0;
}
}
}
if (last_movem_reg != -1)
{
int n_saved
= (n_movem_regs == 1) ? 1 : 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 || n_saved == 1)
&& TARGET_SIDE_EFFECT_PREFIXES)
{
mem
= gen_rtx_MEM (SImode,
plus_constant (stack_pointer_rtx,
-(n_saved * 4 + size)));
set_mem_alias_set (mem, get_frame_alias_set ());
insn = cris_emit_movem_store (mem, GEN_INT (n_saved),
-(n_saved * 4 + size), true);
}
else
{
insn
= gen_rtx_SET (VOIDmode,
stack_pointer_rtx,
plus_constant (stack_pointer_rtx,
-(n_saved * 4 + size)));
insn = emit_insn (insn);
RTX_FRAME_RELATED_P (insn) = 1;
mem = gen_rtx_MEM (SImode, stack_pointer_rtx);
set_mem_alias_set (mem, get_frame_alias_set ());
insn = cris_emit_movem_store (mem, GEN_INT (n_saved), 0, true);
}
framesize += n_saved * 4 + size;
if (cfoa_size)
{
insn = emit_insn (gen_rtx_SET (VOIDmode,
stack_pointer_rtx,
plus_constant (stack_pointer_rtx,
-cfoa_size)));
RTX_FRAME_RELATED_P (insn) = 1;
framesize += cfoa_size;
}
}
else if ((size + cfoa_size) > 0)
{
insn = emit_insn (gen_rtx_SET (VOIDmode,
stack_pointer_rtx,
plus_constant (stack_pointer_rtx,
-(cfoa_size + size))));
RTX_FRAME_RELATED_P (insn) = 1;
framesize += size + cfoa_size;
}
if (got_really_used)
{
rtx got
= gen_rtx_UNSPEC (SImode, gen_rtvec (1, const0_rtx), CRIS_UNSPEC_GOT);
emit_move_insn (pic_offset_table_rtx, got);
emit_insn (gen_rtx_USE (VOIDmode, pic_offset_table_rtx));
}
if (cris_max_stackframe && framesize > cris_max_stackframe)
warning (0, "stackframe too big: %d bytes", framesize);
}
void
cris_expand_epilogue (void)
{
int regno;
int size = get_frame_size ();
int last_movem_reg = -1;
int argspace_offset = current_function_outgoing_args_size;
int pretend = current_function_pretend_args_size;
rtx mem;
bool return_address_on_stack = cris_return_address_on_stack ();
int got_really_used = false;
int n_movem_regs = 0;
if (!TARGET_PROLOGUE_EPILOGUE)
return;
if (current_function_uses_pic_offset_table)
{
push_topmost_sequence ();
got_really_used
= reg_used_between_p (pic_offset_table_rtx, get_insns (), NULL_RTX);
pop_topmost_sequence ();
}
if (TARGET_STACK_ALIGN)
size = TARGET_ALIGN_BY_32 ? (size + 3) & ~3 : (size + 1) & ~1;
for (regno = 0;
regno < FIRST_PSEUDO_REGISTER;
regno++)
if (cris_reg_saved_in_regsave_area (regno, got_really_used))
{
n_movem_regs++;
if (regno == last_movem_reg + 1)
last_movem_reg = regno;
else
break;
}
if (n_movem_regs == 1)
last_movem_reg = -1;
for (regno = FIRST_PSEUDO_REGISTER - 1;
regno > last_movem_reg;
regno--)
if (cris_reg_saved_in_regsave_area (regno, got_really_used))
{
rtx insn;
if (argspace_offset)
{
emit_insn (gen_rtx_SET (VOIDmode,
stack_pointer_rtx,
plus_constant (stack_pointer_rtx,
argspace_offset)));
argspace_offset = 0;
}
mem = gen_rtx_MEM (SImode, gen_rtx_POST_INC (SImode,
stack_pointer_rtx));
set_mem_alias_set (mem, get_frame_alias_set ());
insn = emit_move_insn (gen_rtx_raw_REG (SImode, regno), mem);
REG_NOTES (insn)
= alloc_EXPR_LIST (REG_INC, stack_pointer_rtx, REG_NOTES (insn));
}
if (last_movem_reg != -1)
{
rtx insn;
if (argspace_offset)
{
emit_insn (gen_rtx_SET (VOIDmode,
stack_pointer_rtx,
plus_constant (stack_pointer_rtx,
argspace_offset)));
argspace_offset = 0;
}
mem = gen_rtx_MEM (SImode,
gen_rtx_POST_INC (SImode, stack_pointer_rtx));
set_mem_alias_set (mem, get_frame_alias_set ());
insn
= emit_insn (cris_gen_movem_load (mem,
GEN_INT (last_movem_reg + 1), 0));
if (side_effects_p (PATTERN (insn)))
REG_NOTES (insn)
= alloc_EXPR_LIST (REG_INC, stack_pointer_rtx, REG_NOTES (insn));
}
if (frame_pointer_needed)
{
rtx insn;
emit_insn (gen_cris_frame_deallocated_barrier ());
emit_move_insn (stack_pointer_rtx, frame_pointer_rtx);
mem = gen_rtx_MEM (SImode, gen_rtx_POST_INC (SImode,
stack_pointer_rtx));
set_mem_alias_set (mem, get_frame_alias_set ());
insn = emit_move_insn (frame_pointer_rtx, mem);
REG_NOTES (insn)
= alloc_EXPR_LIST (REG_INC, stack_pointer_rtx, REG_NOTES (insn));
}
else if ((size + argspace_offset) != 0)
{
emit_insn (gen_cris_frame_deallocated_barrier ());
size += argspace_offset;
emit_insn (gen_rtx_SET (VOIDmode,
stack_pointer_rtx,
plus_constant (stack_pointer_rtx, size)));
}
if (return_address_on_stack && pretend == 0)
{
if (current_function_calls_eh_return)
{
rtx mem;
rtx insn;
rtx srpreg = gen_rtx_raw_REG (SImode, CRIS_SRP_REGNUM);
mem = gen_rtx_MEM (SImode,
gen_rtx_POST_INC (SImode,
stack_pointer_rtx));
set_mem_alias_set (mem, get_frame_alias_set ());
insn = emit_move_insn (srpreg, mem);
REG_NOTES (insn)
= alloc_EXPR_LIST (REG_INC, stack_pointer_rtx, REG_NOTES (insn));
emit_insn (gen_addsi3 (stack_pointer_rtx,
stack_pointer_rtx,
gen_rtx_raw_REG (SImode,
CRIS_STACKADJ_REG)));
cris_expand_return (false);
}
else
cris_expand_return (true);
return;
}
if (pretend != 0)
{
if (return_address_on_stack)
{
rtx mem;
rtx srpreg = gen_rtx_raw_REG (SImode, CRIS_SRP_REGNUM);
rtx insn;
mem = gen_rtx_MEM (SImode,
gen_rtx_POST_INC (SImode,
stack_pointer_rtx));
set_mem_alias_set (mem, get_frame_alias_set ());
insn = emit_move_insn (srpreg, mem);
REG_NOTES (insn)
= alloc_EXPR_LIST (REG_INC, stack_pointer_rtx, REG_NOTES (insn));
}
emit_insn (gen_rtx_SET (VOIDmode,
stack_pointer_rtx,
plus_constant (stack_pointer_rtx, pretend)));
}
if (current_function_calls_eh_return)
emit_insn (gen_addsi3 (stack_pointer_rtx,
stack_pointer_rtx,
gen_rtx_raw_REG (SImode,
CRIS_STACKADJ_REG)));
cris_expand_return (false);
}
rtx
cris_gen_movem_load (rtx src, rtx nregs_rtx, int nprefix)
{
int nregs = INTVAL (nregs_rtx);
rtvec vec;
int eltno = 1;
int i;
rtx srcreg = XEXP (src, 0);
unsigned int regno = nregs - 1;
int regno_inc = -1;
if (GET_CODE (srcreg) == POST_INC)
srcreg = XEXP (srcreg, 0);
CRIS_ASSERT (REG_P (srcreg));
if (nregs == 1)
return gen_movsi (gen_rtx_REG (SImode, 0), src);
vec = rtvec_alloc (nprefix + nregs
+ (GET_CODE (XEXP (src, 0)) == POST_INC));
if (GET_CODE (XEXP (src, 0)) == POST_INC)
{
RTVEC_ELT (vec, nprefix + 1)
= gen_rtx_SET (VOIDmode, srcreg, plus_constant (srcreg, nregs * 4));
eltno++;
}
src = replace_equiv_address (src, srcreg);
RTVEC_ELT (vec, nprefix)
= gen_rtx_SET (VOIDmode, gen_rtx_REG (SImode, regno), src);
regno += regno_inc;
for (i = 1; i < nregs; i++, eltno++)
{
RTVEC_ELT (vec, nprefix + eltno)
= gen_rtx_SET (VOIDmode, gen_rtx_REG (SImode, regno),
adjust_address_nv (src, SImode, i * 4));
regno += regno_inc;
}
return gen_rtx_PARALLEL (VOIDmode, vec);
}
rtx
cris_emit_movem_store (rtx dest, rtx nregs_rtx, int increment,
bool frame_related)
{
int nregs = INTVAL (nregs_rtx);
rtvec vec;
int eltno = 1;
int i;
rtx insn;
rtx destreg = XEXP (dest, 0);
unsigned int regno = nregs - 1;
int regno_inc = -1;
if (GET_CODE (destreg) == POST_INC)
increment += nregs * 4;
if (GET_CODE (destreg) == POST_INC || GET_CODE (destreg) == PLUS)
destreg = XEXP (destreg, 0);
CRIS_ASSERT (REG_P (destreg));
if (nregs == 1)
{
rtx mov = gen_rtx_SET (VOIDmode, dest, gen_rtx_REG (SImode, 0));
if (increment == 0)
{
insn = emit_insn (mov);
if (frame_related)
RTX_FRAME_RELATED_P (insn) = 1;
return insn;
}
vec = rtvec_alloc (2);
RTVEC_ELT (vec, 0) = mov;
RTVEC_ELT (vec, 1) = gen_rtx_SET (VOIDmode, destreg,
plus_constant (destreg, increment));
if (frame_related)
{
RTX_FRAME_RELATED_P (mov) = 1;
RTX_FRAME_RELATED_P (RTVEC_ELT (vec, 1)) = 1;
}
}
else
{
vec = rtvec_alloc (nregs + (increment != 0 ? 1 : 0));
RTVEC_ELT (vec, 0)
= gen_rtx_SET (VOIDmode,
replace_equiv_address (dest,
plus_constant (destreg,
increment)),
gen_rtx_REG (SImode, regno));
regno += regno_inc;
if (frame_related)
RTX_FRAME_RELATED_P (RTVEC_ELT (vec, 0)) = 1;
if (increment != 0)
{
RTVEC_ELT (vec, 1)
= gen_rtx_SET (VOIDmode, destreg,
plus_constant (destreg,
increment != 0
? increment : nregs * 4));
eltno++;
if (frame_related)
RTX_FRAME_RELATED_P (RTVEC_ELT (vec, 1)) = 1;
if (GET_CODE (XEXP (dest, 0)) == POST_INC)
dest = replace_equiv_address (dest, destreg);
}
for (i = 1; i < nregs; i++, eltno++)
{
RTVEC_ELT (vec, eltno)
= gen_rtx_SET (VOIDmode, adjust_address_nv (dest, SImode, i * 4),
gen_rtx_REG (SImode, regno));
if (frame_related)
RTX_FRAME_RELATED_P (RTVEC_ELT (vec, eltno)) = 1;
regno += regno_inc;
}
}
insn = emit_insn (gen_rtx_PARALLEL (VOIDmode, vec));
if (frame_related)
{
if (increment != 0)
{
rtx seq = gen_rtx_SEQUENCE (VOIDmode, rtvec_alloc (nregs + 1));
XVECEXP (seq, 0, 0) = XVECEXP (PATTERN (insn), 0, 0);
for (i = 1; i < nregs; i++)
XVECEXP (seq, 0, i) = XVECEXP (PATTERN (insn), 0, i + 1);
XVECEXP (seq, 0, nregs) = XVECEXP (PATTERN (insn), 0, 1);
REG_NOTES (insn)
= gen_rtx_EXPR_LIST (REG_FRAME_RELATED_EXPR, seq,
REG_NOTES (insn));
}
RTX_FRAME_RELATED_P (insn) = 1;
}
return insn;
}
void
cris_expand_pic_call_address (rtx *opp)
{
rtx op = *opp;
gcc_assert (MEM_P (op));
op = XEXP (op, 0);
if (CONSTANT_ADDRESS_P (op) && GET_CODE (op) != CONST_INT)
{
enum cris_pic_symbol_type t = cris_pic_symbol_type_of (op);
CRIS_ASSERT (!no_new_pseudos);
if (t == cris_gotrel_symbol)
op = force_reg (Pmode, op);
else if (t == cris_got_symbol)
{
if (TARGET_AVOID_GOTPLT)
{
rtx tem, rm, ro;
gcc_assert (! no_new_pseudos);
current_function_uses_pic_offset_table = 1;
tem = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, op), CRIS_UNSPEC_PLT);
rm = gen_reg_rtx (Pmode);
emit_move_insn (rm, gen_rtx_CONST (Pmode, tem));
ro = gen_reg_rtx (Pmode);
if (expand_binop (Pmode, add_optab, rm,
pic_offset_table_rtx,
ro, 0, OPTAB_LIB_WIDEN) != ro)
internal_error ("expand_binop failed in movsi got");
op = ro;
}
else
{
rtx tem, mem, rm, ro;
gcc_assert (! no_new_pseudos);
current_function_uses_pic_offset_table = 1;
tem = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, op),
CRIS_UNSPEC_PLTGOTREAD);
rm = gen_reg_rtx (Pmode);
emit_move_insn (rm, gen_rtx_CONST (Pmode, tem));
ro = gen_reg_rtx (Pmode);
if (expand_binop (Pmode, add_optab, rm,
pic_offset_table_rtx,
ro, 0, OPTAB_LIB_WIDEN) != ro)
internal_error ("expand_binop failed in movsi got");
mem = gen_rtx_MEM (Pmode, ro);
set_mem_alias_set (mem, new_alias_set ());
MEM_NOTRAP_P (mem) = 1;
op = mem;
}
}
else
fatal_insn ("Unidentifiable call op", op);
*opp = replace_equiv_address (*opp, op);
}
}
void
cris_order_for_addsi3 (rtx *operands, int n)
{
if (MEM_P (operands[n]))
{
rtx tem = operands[n];
operands[n] = operands[n + 1];
operands[n + 1] = tem;
}
}
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)
{
gcc_assert (GET_CODE (x) == SYMBOL_REF);
if (flag_pic && in_code > 0)
{
const char *origstr = XSTR (x, 0);
const char *str;
str = (* targetm.strip_name_encoding) (origstr);
assemble_name (file, str);
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)
{
assemble_name (file, buf);
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 xconst)
{
switch (GET_CODE (xconst))
{
rtx x;
case UNSPEC:
x = XVECEXP (xconst, 0, 0);
CRIS_ASSERT (GET_CODE (x) == SYMBOL_REF
|| GET_CODE (x) == LABEL_REF
|| GET_CODE (x) == CONST);
output_addr_const (file, x);
switch (XINT (xconst, 1))
{
case CRIS_UNSPEC_PLT:
fprintf (file, ":PLTG");
break;
case CRIS_UNSPEC_GOTREL:
fprintf (file, ":GOTOFF");
break;
case CRIS_UNSPEC_GOTREAD:
if (flag_pic == 1)
fprintf (file, ":GOT16");
else
fprintf (file, ":GOT");
break;
case CRIS_UNSPEC_PLTGOTREAD:
if (flag_pic == 1)
fprintf (file, CRIS_GOTPLT_SUFFIX "16");
else
fprintf (file, CRIS_GOTPLT_SUFFIX);
break;
default:
gcc_unreachable ();
}
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)
{
int stdarg_regs = CRIS_MAX_ARGS_IN_REGS - ca->regs;
cfun->machine->stdarg_regs = stdarg_regs;
*pretend_arg_size = stdarg_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;
}
static tree
cris_md_asm_clobbers (tree outputs, tree inputs, tree in_clobbers)
{
HARD_REG_SET mof_set;
tree clobbers;
tree t;
CLEAR_HARD_REG_SET (mof_set);
SET_HARD_REG_BIT (mof_set, CRIS_MOF_REGNUM);
clobbers
= tree_cons (NULL_TREE,
build_string (strlen (reg_names[CRIS_CC0_REGNUM]),
reg_names[CRIS_CC0_REGNUM]),
in_clobbers);
for (t = outputs; t != NULL; t = TREE_CHAIN (t))
{
tree val = TREE_VALUE (t);
if (strchr (TREE_STRING_POINTER (TREE_VALUE (TREE_PURPOSE (t))),
'h') != NULL
|| tree_overlaps_hard_reg_set (val, &mof_set) != NULL_TREE)
return clobbers;
}
for (t = inputs; t != NULL; t = TREE_CHAIN (t))
{
tree val = TREE_VALUE (t);
if (strchr (TREE_STRING_POINTER (TREE_VALUE (TREE_PURPOSE (t))),
'h') != NULL
|| tree_overlaps_hard_reg_set (val, &mof_set) != NULL_TREE)
return clobbers;
}
return tree_cons (NULL_TREE,
build_string (strlen (reg_names[CRIS_MOF_REGNUM]),
reg_names[CRIS_MOF_REGNUM]),
clobbers);
}
#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"