#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-flags.h"
#include "output.h"
#include "insn-attr.h"
#include "tree.h"
#include "flags.h"
#include "except.h"
#include "function.h"
#include "input.h"
#include "target.h"
#include "target-def.h"
#include "expr.h"
#include "toplev.h"
#include "recog.h"
#include "ggc.h"
#include "integrate.h"
#include "bfin-protos.h"
#include "tm-preds.h"
#include "gt-bfin.h"
rtx bfin_compare_op0, bfin_compare_op1;
extern GTY(()) rtx bfin_cc_rtx;
extern GTY(()) rtx bfin_rets_rtx;
rtx bfin_cc_rtx, bfin_rets_rtx;
int max_arg_registers = 0;
const char *short_reg_names[] = SHORT_REGISTER_NAMES;
const char *high_reg_names[] = HIGH_REGISTER_NAMES;
const char *dregs_pair_names[] = DREGS_PAIR_NAMES;
const char *byte_reg_names[] = BYTE_REGISTER_NAMES;
static int arg_regs[] = FUNCTION_ARG_REGISTERS;
const char *bfin_library_id_string;
static void
bfin_globalize_label (FILE *stream, const char *name)
{
fputs (".global ", stream);
assemble_name (stream, name);
fputc (';',stream);
fputc ('\n',stream);
}
static void
output_file_start (void)
{
FILE *file = asm_out_file;
int i;
fprintf (file, ".file \"%s\";\n", input_filename);
for (i = 0; arg_regs[i] >= 0; i++)
;
max_arg_registers = i;
}
void
conditional_register_usage (void)
{
bfin_cc_rtx = gen_rtx_REG (BImode, REG_CC);
bfin_rets_rtx = gen_rtx_REG (Pmode, REG_RETS);
}
static e_funkind funkind (tree funtype)
{
tree attrs = TYPE_ATTRIBUTES (funtype);
if (lookup_attribute ("interrupt_handler", attrs))
return INTERRUPT_HANDLER;
else if (lookup_attribute ("exception_handler", attrs))
return EXCPT_HANDLER;
else if (lookup_attribute ("nmi_handler", attrs))
return NMI_HANDLER;
else
return SUBROUTINE;
}
static int
n_dregs_to_save (void)
{
unsigned i;
for (i = REG_R0; i <= REG_R7; i++)
{
if (regs_ever_live[i] && ! call_used_regs[i])
return REG_R7 - i + 1;
if (current_function_calls_eh_return)
{
unsigned j;
for (j = 0; ; j++)
{
unsigned test = EH_RETURN_DATA_REGNO (j);
if (test == INVALID_REGNUM)
break;
if (test == i)
return REG_R7 - i + 1;
}
}
}
return 0;
}
static int
n_pregs_to_save (void)
{
unsigned i;
for (i = REG_P0; i <= REG_P5; i++)
if ((regs_ever_live[i] && ! call_used_regs[i])
|| (i == PIC_OFFSET_TABLE_REGNUM
&& (current_function_uses_pic_offset_table
|| (TARGET_ID_SHARED_LIBRARY && ! current_function_is_leaf))))
return REG_P5 - i + 1;
return 0;
}
static bool
must_save_fp_p (void)
{
return (frame_pointer_needed || regs_ever_live[REG_FP]);
}
static bool
stack_frame_needed_p (void)
{
if (current_function_calls_eh_return)
return true;
return frame_pointer_needed;
}
static void
expand_prologue_reg_save (rtx spreg, int saveall)
{
int ndregs = saveall ? 8 : n_dregs_to_save ();
int npregs = saveall ? 6 : n_pregs_to_save ();
int dregno = REG_R7 + 1 - ndregs;
int pregno = REG_P5 + 1 - npregs;
int total = ndregs + npregs;
int i;
rtx pat, insn, val;
if (total == 0)
return;
val = GEN_INT (-total * 4);
pat = gen_rtx_PARALLEL (VOIDmode, rtvec_alloc (total + 2));
XVECEXP (pat, 0, 0) = gen_rtx_UNSPEC (VOIDmode, gen_rtvec (1, val),
UNSPEC_PUSH_MULTIPLE);
XVECEXP (pat, 0, total + 1) = gen_rtx_SET (VOIDmode, spreg,
gen_rtx_PLUS (Pmode, spreg,
val));
RTX_FRAME_RELATED_P (XVECEXP (pat, 0, total + 1)) = 1;
for (i = 0; i < total; i++)
{
rtx memref = gen_rtx_MEM (word_mode,
gen_rtx_PLUS (Pmode, spreg,
GEN_INT (- i * 4 - 4)));
rtx subpat;
if (ndregs > 0)
{
subpat = gen_rtx_SET (VOIDmode, memref, gen_rtx_REG (word_mode,
dregno++));
ndregs--;
}
else
{
subpat = gen_rtx_SET (VOIDmode, memref, gen_rtx_REG (word_mode,
pregno++));
npregs++;
}
XVECEXP (pat, 0, i + 1) = subpat;
RTX_FRAME_RELATED_P (subpat) = 1;
}
insn = emit_insn (pat);
RTX_FRAME_RELATED_P (insn) = 1;
}
static void
expand_epilogue_reg_restore (rtx spreg, int saveall)
{
int ndregs = saveall ? 8 : n_dregs_to_save ();
int npregs = saveall ? 6 : n_pregs_to_save ();
int total = ndregs + npregs;
int i, regno;
rtx pat, insn;
if (total == 0)
return;
pat = gen_rtx_PARALLEL (VOIDmode, rtvec_alloc (total + 1));
XVECEXP (pat, 0, 0) = gen_rtx_SET (VOIDmode, spreg,
gen_rtx_PLUS (Pmode, spreg,
GEN_INT (total * 4)));
if (npregs > 0)
regno = REG_P5 + 1;
else
regno = REG_R7 + 1;
for (i = 0; i < total; i++)
{
rtx addr = (i > 0
? gen_rtx_PLUS (Pmode, spreg, GEN_INT (i * 4))
: spreg);
rtx memref = gen_rtx_MEM (word_mode, addr);
regno--;
XVECEXP (pat, 0, i + 1)
= gen_rtx_SET (VOIDmode, gen_rtx_REG (word_mode, regno), memref);
if (npregs > 0)
{
if (--npregs == 0)
regno = REG_R7 + 1;
}
}
insn = emit_insn (pat);
RTX_FRAME_RELATED_P (insn) = 1;
}
static void
setup_incoming_varargs (CUMULATIVE_ARGS *cum,
enum machine_mode mode ATTRIBUTE_UNUSED,
tree type ATTRIBUTE_UNUSED, int *pretend_size,
int no_rtl)
{
rtx mem;
int i;
if (no_rtl)
return;
for (i = cum->words + 1; i < max_arg_registers; i++)
{
mem = gen_rtx_MEM (Pmode,
plus_constant (arg_pointer_rtx, (i * UNITS_PER_WORD)));
emit_move_insn (mem, gen_rtx_REG (Pmode, i));
}
*pretend_size = 0;
}
int
bfin_frame_pointer_required (void)
{
e_funkind fkind = funkind (TREE_TYPE (current_function_decl));
if (fkind != SUBROUTINE)
return 1;
if (TARGET_OMIT_LEAF_FRAME_POINTER && ! current_function_is_leaf)
return 1;
return 0;
}
static int
n_regs_saved_by_prologue (void)
{
e_funkind fkind = funkind (TREE_TYPE (current_function_decl));
int n = n_dregs_to_save () + n_pregs_to_save ();
if (stack_frame_needed_p ())
n += 2;
else
{
if (must_save_fp_p ())
n++;
if (! current_function_is_leaf)
n++;
}
if (fkind != SUBROUTINE)
{
tree attrs = TYPE_ATTRIBUTES (TREE_TYPE (current_function_decl));
tree all = lookup_attribute ("saveall", attrs);
int i;
n++;
if (lookup_attribute ("nesting", attrs))
n++;
for (i = REG_P7 + 1; i < REG_CC; i++)
if (all
|| regs_ever_live[i]
|| (!leaf_function_p () && call_used_regs[i]))
n += i == REG_A0 || i == REG_A1 ? 2 : 1;
}
return n;
}
HOST_WIDE_INT
bfin_initial_elimination_offset (int from, int to)
{
HOST_WIDE_INT offset = 0;
if (from == ARG_POINTER_REGNUM)
offset = n_regs_saved_by_prologue () * 4;
if (to == STACK_POINTER_REGNUM)
{
if (current_function_outgoing_args_size >= FIXED_STACK_AREA)
offset += current_function_outgoing_args_size;
else if (current_function_outgoing_args_size)
offset += FIXED_STACK_AREA;
offset += get_frame_size ();
}
return offset;
}
static void
frame_related_constant_load (rtx reg, HOST_WIDE_INT constant)
{
rtx insn;
rtx cst = GEN_INT (constant);
if (constant >= -32768 && constant < 65536)
insn = emit_move_insn (reg, cst);
else
{
insn = emit_insn (gen_movsi_high (reg, cst));
RTX_FRAME_RELATED_P (insn) = 1;
insn = emit_insn (gen_movsi_low (reg, reg, cst));
}
RTX_FRAME_RELATED_P (insn) = 1;
}
static void
add_to_sp (rtx spreg, HOST_WIDE_INT value, int frame)
{
if (value == 0)
return;
if (value > 120 || value < -120)
{
rtx tmpreg = gen_rtx_REG (SImode, REG_P1);
rtx insn;
if (frame)
frame_related_constant_load (tmpreg, value);
else
{
insn = emit_move_insn (tmpreg, GEN_INT (value));
if (frame)
RTX_FRAME_RELATED_P (insn) = 1;
}
insn = emit_insn (gen_addsi3 (spreg, spreg, tmpreg));
if (frame)
RTX_FRAME_RELATED_P (insn) = 1;
}
else
do
{
int size = value;
rtx insn;
if (size > 60)
size = 60;
else if (size < -60)
size = -60;
insn = emit_insn (gen_addsi3 (spreg, spreg, GEN_INT (size)));
if (frame)
RTX_FRAME_RELATED_P (insn) = 1;
value -= size;
}
while (value != 0);
}
static void
emit_link_insn (rtx spreg, HOST_WIDE_INT frame_size)
{
HOST_WIDE_INT link_size = frame_size;
rtx insn;
int i;
if (link_size > 262140)
link_size = 262140;
insn = emit_insn (gen_link (GEN_INT (-8 - link_size)));
RTX_FRAME_RELATED_P (insn) = 1;
for (i = 0; i < XVECLEN (PATTERN (insn), 0); i++)
{
rtx set = XVECEXP (PATTERN (insn), 0, i);
if (GET_CODE (set) != SET)
abort ();
RTX_FRAME_RELATED_P (set) = 1;
}
frame_size -= link_size;
if (frame_size > 0)
{
rtx tmpreg = gen_rtx_REG (Pmode, REG_P1);
frame_related_constant_load (tmpreg, -frame_size);
insn = emit_insn (gen_addsi3 (spreg, spreg, tmpreg));
RTX_FRAME_RELATED_P (insn) = 1;
}
}
static HOST_WIDE_INT
arg_area_size (void)
{
if (current_function_outgoing_args_size)
{
if (current_function_outgoing_args_size >= FIXED_STACK_AREA)
return current_function_outgoing_args_size;
else
return FIXED_STACK_AREA;
}
return 0;
}
static void
do_link (rtx spreg, HOST_WIDE_INT frame_size)
{
frame_size += arg_area_size ();
if (stack_frame_needed_p ()
|| (must_save_fp_p () && ! current_function_is_leaf))
emit_link_insn (spreg, frame_size);
else
{
if (! current_function_is_leaf)
{
rtx pat = gen_movsi (gen_rtx_MEM (Pmode,
gen_rtx_PRE_DEC (Pmode, spreg)),
bfin_rets_rtx);
rtx insn = emit_insn (pat);
RTX_FRAME_RELATED_P (insn) = 1;
}
if (must_save_fp_p ())
{
rtx pat = gen_movsi (gen_rtx_MEM (Pmode,
gen_rtx_PRE_DEC (Pmode, spreg)),
gen_rtx_REG (Pmode, REG_FP));
rtx insn = emit_insn (pat);
RTX_FRAME_RELATED_P (insn) = 1;
}
add_to_sp (spreg, -frame_size, 1);
}
}
static void
do_unlink (rtx spreg, HOST_WIDE_INT frame_size)
{
frame_size += arg_area_size ();
if (stack_frame_needed_p ())
emit_insn (gen_unlink ());
else
{
rtx postinc = gen_rtx_MEM (Pmode, gen_rtx_POST_INC (Pmode, spreg));
add_to_sp (spreg, frame_size, 0);
if (must_save_fp_p ())
{
rtx fpreg = gen_rtx_REG (Pmode, REG_FP);
emit_move_insn (fpreg, postinc);
emit_insn (gen_rtx_USE (VOIDmode, fpreg));
}
if (! current_function_is_leaf)
{
emit_move_insn (bfin_rets_rtx, postinc);
emit_insn (gen_rtx_USE (VOIDmode, bfin_rets_rtx));
}
}
}
static void
expand_interrupt_handler_prologue (rtx spreg, e_funkind fkind)
{
int i;
HOST_WIDE_INT frame_size = get_frame_size ();
rtx predec1 = gen_rtx_PRE_DEC (SImode, spreg);
rtx predec = gen_rtx_MEM (SImode, predec1);
rtx insn;
tree attrs = TYPE_ATTRIBUTES (TREE_TYPE (current_function_decl));
tree all = lookup_attribute ("saveall", attrs);
tree kspisusp = lookup_attribute ("kspisusp", attrs);
if (kspisusp)
{
insn = emit_move_insn (spreg, gen_rtx_REG (Pmode, REG_USP));
RTX_FRAME_RELATED_P (insn) = 1;
}
if (fkind == EXCPT_HANDLER)
{
insn = emit_insn (gen_addsi3 (spreg, spreg, GEN_INT (-12)));
RTX_FRAME_RELATED_P (insn) = 1;
}
insn = emit_move_insn (predec, gen_rtx_REG (SImode, REG_ASTAT));
RTX_FRAME_RELATED_P (insn) = 1;
expand_prologue_reg_save (spreg, all != NULL_TREE);
for (i = REG_P7 + 1; i < REG_CC; i++)
if (all
|| regs_ever_live[i]
|| (!leaf_function_p () && call_used_regs[i]))
{
if (i == REG_A0 || i == REG_A1)
insn = emit_move_insn (gen_rtx_MEM (PDImode, predec1),
gen_rtx_REG (PDImode, i));
else
insn = emit_move_insn (predec, gen_rtx_REG (SImode, i));
RTX_FRAME_RELATED_P (insn) = 1;
}
if (lookup_attribute ("nesting", attrs))
{
rtx srcreg = gen_rtx_REG (Pmode, (fkind == EXCPT_HANDLER ? REG_RETX
: fkind == NMI_HANDLER ? REG_RETN
: REG_RETI));
insn = emit_move_insn (predec, srcreg);
RTX_FRAME_RELATED_P (insn) = 1;
}
do_link (spreg, frame_size);
if (fkind == EXCPT_HANDLER)
{
rtx r0reg = gen_rtx_REG (SImode, REG_R0);
rtx r1reg = gen_rtx_REG (SImode, REG_R1);
rtx r2reg = gen_rtx_REG (SImode, REG_R2);
rtx insn;
insn = emit_move_insn (r0reg, gen_rtx_REG (SImode, REG_SEQSTAT));
REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_MAYBE_DEAD, const0_rtx,
NULL_RTX);
insn = emit_insn (gen_ashrsi3 (r0reg, r0reg, GEN_INT (26)));
REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_MAYBE_DEAD, const0_rtx,
NULL_RTX);
insn = emit_insn (gen_ashlsi3 (r0reg, r0reg, GEN_INT (26)));
REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_MAYBE_DEAD, const0_rtx,
NULL_RTX);
insn = emit_move_insn (r1reg, spreg);
REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_MAYBE_DEAD, const0_rtx,
NULL_RTX);
insn = emit_move_insn (r2reg, gen_rtx_REG (Pmode, REG_FP));
REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_MAYBE_DEAD, const0_rtx,
NULL_RTX);
insn = emit_insn (gen_addsi3 (r2reg, r2reg, GEN_INT (8)));
REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_MAYBE_DEAD, const0_rtx,
NULL_RTX);
}
}
static void
expand_interrupt_handler_epilogue (rtx spreg, e_funkind fkind)
{
int i;
rtx postinc1 = gen_rtx_POST_INC (SImode, spreg);
rtx postinc = gen_rtx_MEM (SImode, postinc1);
tree attrs = TYPE_ATTRIBUTES (TREE_TYPE (current_function_decl));
tree all = lookup_attribute ("saveall", attrs);
MEM_VOLATILE_P (postinc) = 1;
do_unlink (spreg, get_frame_size ());
if (lookup_attribute ("nesting", attrs))
{
rtx srcreg = gen_rtx_REG (Pmode, (fkind == EXCPT_HANDLER ? REG_RETX
: fkind == NMI_HANDLER ? REG_RETN
: REG_RETI));
emit_move_insn (srcreg, postinc);
}
for (i = REG_CC - 1; i > REG_P7; i--)
if (all
|| regs_ever_live[i]
|| (!leaf_function_p () && call_used_regs[i]))
{
if (i == REG_A0 || i == REG_A1)
{
rtx mem = gen_rtx_MEM (PDImode, postinc1);
MEM_VOLATILE_P (mem) = 1;
emit_move_insn (gen_rtx_REG (PDImode, i), mem);
}
else
emit_move_insn (gen_rtx_REG (SImode, i), postinc);
}
expand_epilogue_reg_restore (spreg, all != NULL_TREE);
emit_move_insn (gen_rtx_REG (SImode, REG_ASTAT), postinc);
if (fkind == EXCPT_HANDLER)
emit_insn (gen_addsi3 (spreg, spreg, GEN_INT (12)));
emit_jump_insn (gen_return_internal (GEN_INT (fkind)));
}
void
bfin_expand_prologue (void)
{
rtx insn;
HOST_WIDE_INT frame_size = get_frame_size ();
rtx spreg = gen_rtx_REG (Pmode, REG_SP);
e_funkind fkind = funkind (TREE_TYPE (current_function_decl));
if (fkind != SUBROUTINE)
{
expand_interrupt_handler_prologue (spreg, fkind);
return;
}
expand_prologue_reg_save (spreg, 0);
do_link (spreg, frame_size);
if (TARGET_ID_SHARED_LIBRARY
&& (current_function_uses_pic_offset_table
|| !current_function_is_leaf))
{
rtx addr;
if (bfin_library_id_string)
addr = plus_constant (pic_offset_table_rtx, atoi (bfin_library_id_string));
else
addr = gen_rtx_PLUS (Pmode, pic_offset_table_rtx,
gen_rtx_UNSPEC (Pmode, gen_rtvec (1, const0_rtx),
UNSPEC_LIBRARY_OFFSET));
insn = emit_insn (gen_movsi (pic_offset_table_rtx,
gen_rtx_MEM (Pmode, addr)));
REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_MAYBE_DEAD, const0_rtx, NULL);
}
}
void
bfin_expand_epilogue (int need_return, int eh_return)
{
rtx spreg = gen_rtx_REG (Pmode, REG_SP);
e_funkind fkind = funkind (TREE_TYPE (current_function_decl));
if (fkind != SUBROUTINE)
{
expand_interrupt_handler_epilogue (spreg, fkind);
return;
}
do_unlink (spreg, get_frame_size ());
expand_epilogue_reg_restore (spreg, 0);
if (! need_return)
return;
if (eh_return)
emit_insn (gen_addsi3 (spreg, spreg, gen_rtx_REG (Pmode, REG_P2)));
emit_jump_insn (gen_return_internal (GEN_INT (SUBROUTINE)));
}
int
bfin_hard_regno_rename_ok (unsigned int old_reg ATTRIBUTE_UNUSED,
unsigned int new_reg)
{
if (funkind (TREE_TYPE (current_function_decl)) != SUBROUTINE
&& !regs_ever_live[new_reg])
return 0;
return 1;
}
rtx
bfin_return_addr_rtx (int count)
{
if (count != 0)
return const0_rtx;
return get_hard_reg_initial_val (Pmode, REG_RETS);
}
rtx
legitimize_address (rtx x ATTRIBUTE_UNUSED, rtx oldx ATTRIBUTE_UNUSED,
enum machine_mode mode ATTRIBUTE_UNUSED)
{
return NULL_RTX;
}
int
effective_address_32bit_p (rtx op, enum machine_mode mode)
{
HOST_WIDE_INT offset;
mode = GET_MODE (op);
op = XEXP (op, 0);
if (REG_P (op) || GET_CODE (op) == POST_INC
|| GET_CODE (op) == PRE_DEC || GET_CODE (op) == POST_DEC)
return 0;
if (GET_CODE (op) != PLUS)
abort ();
offset = INTVAL (XEXP (op, 1));
if (GET_MODE_SIZE (mode) == 1)
return 1;
if (GET_MODE_SIZE (mode) == 4)
{
if (XEXP (op, 0) == frame_pointer_rtx)
return offset < -128 || offset > 60;
return offset < 0 || offset > 60;
}
return offset < 0 || offset > 30;
}
static int
bfin_address_cost (rtx addr ATTRIBUTE_UNUSED)
{
return 1;
}
void
print_address_operand (FILE *file, rtx x)
{
if (GET_CODE (x) == MEM)
abort ();
switch (GET_CODE (x))
{
case PLUS:
output_address (XEXP (x, 0));
fprintf (file, "+");
output_address (XEXP (x, 1));
break;
case PRE_DEC:
fprintf (file, "--");
output_address (XEXP (x, 0));
break;
case POST_INC:
output_address (XEXP (x, 0));
fprintf (file, "++");
break;
case POST_DEC:
output_address (XEXP (x, 0));
fprintf (file, "--");
break;
default:
print_operand (file, x, 0);
}
}
void
print_operand (FILE *file, rtx x, char code)
{
enum machine_mode mode = GET_MODE (x);
switch (code)
{
case 'j':
switch (GET_CODE (x))
{
case EQ:
fprintf (file, "e");
break;
case NE:
fprintf (file, "ne");
break;
case GT:
fprintf (file, "g");
break;
case LT:
fprintf (file, "l");
break;
case GE:
fprintf (file, "ge");
break;
case LE:
fprintf (file, "le");
break;
case GTU:
fprintf (file, "g");
break;
case LTU:
fprintf (file, "l");
break;
case GEU:
fprintf (file, "ge");
break;
case LEU:
fprintf (file, "le");
break;
default:
output_operand_lossage ("invalid %%j value");
}
break;
case 'J':
switch (GET_CODE(x))
{
case EQ:
fprintf (file, "ne");
break;
case NE:
fprintf (file, "e");
break;
case GT:
fprintf (file, "le");
break;
case LT:
fprintf (file, "ge");
break;
case GE:
fprintf (file, "l");
break;
case LE:
fprintf (file, "g");
break;
case GTU:
fprintf (file, "le");
break;
case LTU:
fprintf (file, "ge");
break;
case GEU:
fprintf (file, "l");
break;
case LEU:
fprintf (file, "g");
break;
default:
output_operand_lossage ("invalid %%J value");
}
break;
default:
switch (GET_CODE (x))
{
case REG:
if (code == 'h')
{
gcc_assert (REGNO (x) < 32);
fprintf (file, "%s", short_reg_names[REGNO (x)]);
break;
}
else if (code == 'd')
{
gcc_assert (REGNO (x) < 32);
fprintf (file, "%s", high_reg_names[REGNO (x)]);
break;
}
else if (code == 'w')
{
gcc_assert (REGNO (x) == REG_A0 || REGNO (x) == REG_A1);
fprintf (file, "%s.w", reg_names[REGNO (x)]);
}
else if (code == 'x')
{
gcc_assert (REGNO (x) == REG_A0 || REGNO (x) == REG_A1);
fprintf (file, "%s.x", reg_names[REGNO (x)]);
}
else if (code == 'D')
{
fprintf (file, "%s", dregs_pair_names[REGNO (x)]);
}
else if (code == 'H')
{
gcc_assert (mode == DImode || mode == DFmode);
gcc_assert (REG_P (x));
fprintf (file, "%s", reg_names[REGNO (x) + 1]);
}
else if (code == 'T')
{
if (REGNO (x) > 7)
abort ();
fprintf (file, "%s", byte_reg_names[REGNO (x)]);
}
else
fprintf (file, "%s", reg_names[REGNO (x)]);
break;
case MEM:
fputc ('[', file);
x = XEXP (x,0);
print_address_operand (file, x);
fputc (']', file);
break;
case CONST_INT:
if (code == 'd')
x = GEN_INT ((INTVAL (x) >> 16) & 0xffff);
else if (code == 'h')
x = GEN_INT (INTVAL (x) & 0xffff);
else if (code == 'X')
x = GEN_INT (exact_log2 (0xffffffff & INTVAL (x)));
else if (code == 'Y')
x = GEN_INT (exact_log2 (0xffffffff & ~INTVAL (x)));
else if (code == 'Z')
x = GEN_INT (-8 - INTVAL (x));
case SYMBOL_REF:
output_addr_const (file, x);
if (code == 'G' && flag_pic)
fprintf (file, "@GOT");
break;
case CONST_DOUBLE:
output_operand_lossage ("invalid const_double operand");
break;
case UNSPEC:
if (XINT (x, 1) == UNSPEC_MOVE_PIC)
{
output_addr_const (file, XVECEXP (x, 0, 0));
fprintf (file, "@GOT");
}
else if (XINT (x, 1) == UNSPEC_LIBRARY_OFFSET)
fprintf (file, "_current_shared_library_p5_offset_");
else
abort ();
break;
default:
output_addr_const (file, x);
}
}
}
void
init_cumulative_args (CUMULATIVE_ARGS *cum, tree fntype ATTRIBUTE_UNUSED,
rtx libname ATTRIBUTE_UNUSED)
{
static CUMULATIVE_ARGS zero_cum;
*cum = zero_cum;
cum->nregs = max_arg_registers;
cum->arg_regs = arg_regs;
return;
}
void
function_arg_advance (CUMULATIVE_ARGS *cum, enum machine_mode mode, tree type,
int named ATTRIBUTE_UNUSED)
{
int count, bytes, words;
bytes = (mode == BLKmode) ? int_size_in_bytes (type) : GET_MODE_SIZE (mode);
words = (bytes + UNITS_PER_WORD - 1) / UNITS_PER_WORD;
cum->words += words;
cum->nregs -= words;
if (cum->nregs <= 0)
{
cum->nregs = 0;
cum->arg_regs = NULL;
}
else
{
for (count = 1; count <= words; count++)
cum->arg_regs++;
}
return;
}
struct rtx_def *
function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode, tree type,
int named ATTRIBUTE_UNUSED)
{
int bytes
= (mode == BLKmode) ? int_size_in_bytes (type) : GET_MODE_SIZE (mode);
if (bytes == -1)
return NULL_RTX;
if (cum->nregs)
return gen_rtx_REG (mode, *(cum->arg_regs));
return NULL_RTX;
}
static int
bfin_arg_partial_bytes (CUMULATIVE_ARGS *cum, enum machine_mode mode,
tree type ATTRIBUTE_UNUSED,
bool named ATTRIBUTE_UNUSED)
{
int bytes
= (mode == BLKmode) ? int_size_in_bytes (type) : GET_MODE_SIZE (mode);
int bytes_left = cum->nregs * UNITS_PER_WORD;
if (bytes == -1)
return 0;
if (bytes_left == 0)
return 0;
if (bytes > bytes_left)
return bytes_left;
return 0;
}
static bool
bfin_pass_by_reference (CUMULATIVE_ARGS *cum ATTRIBUTE_UNUSED,
enum machine_mode mode ATTRIBUTE_UNUSED,
tree type, bool named ATTRIBUTE_UNUSED)
{
return type && TREE_CODE (TYPE_SIZE (type)) != INTEGER_CST;
}
int
bfin_return_in_memory (tree type)
{
int size;
enum machine_mode mode = TYPE_MODE (type);
if (mode == BLKmode)
return 1;
size = int_size_in_bytes (type);
if (VECTOR_MODE_P (mode) || mode == TImode)
{
if (size < 8)
return 0;
if (size == 8 || size == 16)
return 1;
}
if (size > 12)
return 1;
return 0;
}
static rtx
bfin_struct_value_rtx (tree fntype ATTRIBUTE_UNUSED,
int incoming ATTRIBUTE_UNUSED)
{
return gen_rtx_REG (Pmode, REG_P0);
}
bool
function_arg_regno_p (int n)
{
int i;
for (i = 0; arg_regs[i] != -1; i++)
if (n == arg_regs[i])
return true;
return false;
}
int
symbolic_reference_mentioned_p (rtx op)
{
register const char *fmt;
register int i;
if (GET_CODE (op) == SYMBOL_REF || GET_CODE (op) == LABEL_REF)
return 1;
fmt = GET_RTX_FORMAT (GET_CODE (op));
for (i = GET_RTX_LENGTH (GET_CODE (op)) - 1; i >= 0; i--)
{
if (fmt[i] == 'E')
{
register int j;
for (j = XVECLEN (op, i) - 1; j >= 0; j--)
if (symbolic_reference_mentioned_p (XVECEXP (op, i, j)))
return 1;
}
else if (fmt[i] == 'e' && symbolic_reference_mentioned_p (XEXP (op, i)))
return 1;
}
return 0;
}
static bool
bfin_function_ok_for_sibcall (tree decl ATTRIBUTE_UNUSED,
tree exp ATTRIBUTE_UNUSED)
{
return true;
}
void
initialize_trampoline (tramp, fnaddr, cxt)
rtx tramp, fnaddr, cxt;
{
rtx t1 = copy_to_reg (fnaddr);
rtx t2 = copy_to_reg (cxt);
rtx addr;
addr = memory_address (Pmode, plus_constant (tramp, 2));
emit_move_insn (gen_rtx_MEM (HImode, addr), gen_lowpart (HImode, t1));
emit_insn (gen_ashrsi3 (t1, t1, GEN_INT (16)));
addr = memory_address (Pmode, plus_constant (tramp, 6));
emit_move_insn (gen_rtx_MEM (HImode, addr), gen_lowpart (HImode, t1));
addr = memory_address (Pmode, plus_constant (tramp, 10));
emit_move_insn (gen_rtx_MEM (HImode, addr), gen_lowpart (HImode, t2));
emit_insn (gen_ashrsi3 (t2, t2, GEN_INT (16)));
addr = memory_address (Pmode, plus_constant (tramp, 14));
emit_move_insn (gen_rtx_MEM (HImode, addr), gen_lowpart (HImode, t2));
}
rtx
legitimize_pic_address (rtx orig, rtx reg)
{
rtx addr = orig;
rtx new = orig;
if (GET_CODE (addr) == SYMBOL_REF || GET_CODE (addr) == LABEL_REF)
{
if (GET_CODE (addr) == SYMBOL_REF && CONSTANT_POOL_ADDRESS_P (addr))
reg = new = orig;
else
{
if (reg == 0)
{
if (no_new_pseudos)
abort ();
reg = gen_reg_rtx (Pmode);
}
if (flag_pic == 2)
{
emit_insn (gen_movsi_high_pic (reg, addr));
emit_insn (gen_movsi_low_pic (reg, reg, addr));
emit_insn (gen_addsi3 (reg, reg, pic_offset_table_rtx));
new = gen_rtx_MEM (Pmode, reg);
}
else
{
rtx tmp = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr),
UNSPEC_MOVE_PIC);
new = gen_rtx_MEM (Pmode,
gen_rtx_PLUS (Pmode, pic_offset_table_rtx,
tmp));
}
emit_move_insn (reg, new);
}
current_function_uses_pic_offset_table = 1;
return reg;
}
else if (GET_CODE (addr) == CONST || GET_CODE (addr) == PLUS)
{
rtx base;
if (GET_CODE (addr) == CONST)
{
addr = XEXP (addr, 0);
if (GET_CODE (addr) != PLUS)
abort ();
}
if (XEXP (addr, 0) == pic_offset_table_rtx)
return orig;
if (reg == 0)
{
if (no_new_pseudos)
abort ();
reg = gen_reg_rtx (Pmode);
}
base = legitimize_pic_address (XEXP (addr, 0), reg);
addr = legitimize_pic_address (XEXP (addr, 1),
base == reg ? NULL_RTX : reg);
if (GET_CODE (addr) == CONST_INT)
{
if (! reload_in_progress && ! reload_completed)
addr = force_reg (Pmode, addr);
else
abort ();
}
if (GET_CODE (addr) == PLUS && CONSTANT_P (XEXP (addr, 1)))
{
base = gen_rtx_PLUS (Pmode, base, XEXP (addr, 0));
addr = XEXP (addr, 1);
}
return gen_rtx_PLUS (Pmode, base, addr);
}
return new;
}
void
emit_pic_move (rtx *operands, enum machine_mode mode ATTRIBUTE_UNUSED)
{
rtx temp = reload_in_progress ? operands[0] : gen_reg_rtx (Pmode);
if (GET_CODE (operands[0]) == MEM && SYMBOLIC_CONST (operands[1]))
operands[1] = force_reg (SImode, operands[1]);
else
operands[1] = legitimize_pic_address (operands[1], temp);
}
void
expand_move (rtx *operands, enum machine_mode mode)
{
if (flag_pic && SYMBOLIC_CONST (operands[1]))
emit_pic_move (operands, mode);
else if ((reload_in_progress | reload_completed) == 0
&& GET_CODE (operands[0]) == MEM
&& GET_CODE (operands[1]) != REG)
operands[1] = force_reg (mode, operands[1]);
}
void
split_di (rtx operands[], int num, rtx lo_half[], rtx hi_half[])
{
while (num--)
{
rtx op = operands[num];
if (GET_CODE (op) == MEM)
{
lo_half[num] = adjust_address (op, SImode, 0);
hi_half[num] = adjust_address (op, SImode, 4);
}
else
{
lo_half[num] = simplify_gen_subreg (SImode, op,
GET_MODE (op) == VOIDmode
? DImode : GET_MODE (op), 0);
hi_half[num] = simplify_gen_subreg (SImode, op,
GET_MODE (op) == VOIDmode
? DImode : GET_MODE (op), 4);
}
}
}
void
bfin_expand_call (rtx retval, rtx fnaddr, rtx callarg1, int sibcall)
{
rtx use = NULL, call;
if (flag_pic
&& GET_CODE (XEXP (fnaddr, 0)) == SYMBOL_REF
&& ! SYMBOL_REF_LOCAL_P (XEXP (fnaddr, 0)))
use_reg (&use, pic_offset_table_rtx);
if (! call_insn_operand (XEXP (fnaddr, 0), Pmode))
{
fnaddr = copy_to_mode_reg (Pmode, XEXP (fnaddr, 0));
fnaddr = gen_rtx_MEM (Pmode, fnaddr);
}
call = gen_rtx_CALL (VOIDmode, fnaddr, callarg1);
if (retval)
call = gen_rtx_SET (VOIDmode, retval, call);
if (sibcall)
{
rtx pat = gen_rtx_PARALLEL (VOIDmode, rtvec_alloc (2));
XVECEXP (pat, 0, 0) = call;
XVECEXP (pat, 0, 1) = gen_rtx_RETURN (VOIDmode);
call = pat;
}
call = emit_call_insn (call);
if (use)
CALL_INSN_FUNCTION_USAGE (call) = use;
}
int
hard_regno_mode_ok (int regno, enum machine_mode mode)
{
enum reg_class class = REGNO_REG_CLASS (regno);
if (mode == CCmode)
return 0;
if (mode == V2HImode)
return D_REGNO_P (regno);
if (class == CCREGS)
return mode == BImode;
if (mode == PDImode)
return regno == REG_A0 || regno == REG_A1;
if (mode == SImode
&& TEST_HARD_REG_BIT (reg_class_contents[PROLOGUE_REGS], regno))
return 1;
return TEST_HARD_REG_BIT (reg_class_contents[MOST_REGS], regno);
}
static bool
bfin_vector_mode_supported_p (enum machine_mode mode)
{
return mode == V2HImode;
}
int
bfin_register_move_cost (enum machine_mode mode ATTRIBUTE_UNUSED,
enum reg_class class1, enum reg_class class2)
{
if (optimize_size)
return 2;
if (class1 == DREGS && class2 != DREGS)
return 2 * 2;
return 2;
}
int
bfin_memory_move_cost (enum machine_mode mode ATTRIBUTE_UNUSED,
enum reg_class class,
int in ATTRIBUTE_UNUSED)
{
if (! reg_class_subset_p (class, DPREGS))
return 10;
return 8;
}
enum reg_class
secondary_input_reload_class (enum reg_class class, enum machine_mode mode,
rtx x)
{
enum reg_class default_class = GET_MODE_SIZE (mode) >= 4 ? DPREGS : DREGS;
enum reg_class x_class = NO_REGS;
enum rtx_code code = GET_CODE (x);
if (code == SUBREG)
x = SUBREG_REG (x), code = GET_CODE (x);
if (REG_P (x))
{
int regno = REGNO (x);
if (regno >= FIRST_PSEUDO_REGISTER)
regno = reg_renumber[regno];
if (regno == -1)
code = MEM;
else
x_class = REGNO_REG_CLASS (regno);
}
if (fp_plus_const_operand (x, mode))
{
rtx op2 = XEXP (x, 1);
int large_constant_p = ! CONST_7BIT_IMM_P (INTVAL (op2));
if (class == PREGS || class == PREGS_CLOBBERED)
return NO_REGS;
if (class == DREGS || class == DPREGS)
return large_constant_p ? PREGS : NO_REGS;
return PREGS;
}
if (x_class == AREGS)
return class == DREGS || class == AREGS ? NO_REGS : DREGS;
if (class == AREGS)
{
if (x != const0_rtx && x_class != DREGS)
return DREGS;
else
return NO_REGS;
}
if (class == CCREGS && x_class != DREGS)
return DREGS;
if (x_class == CCREGS && class != DREGS)
return DREGS;
if (code == MEM)
if (! reg_class_subset_p (class, default_class))
return default_class;
return NO_REGS;
}
enum reg_class
secondary_output_reload_class (enum reg_class class, enum machine_mode mode,
rtx x)
{
return secondary_input_reload_class (class, mode, x);
}
void
override_options (void)
{
if (TARGET_OMIT_LEAF_FRAME_POINTER)
flag_omit_frame_pointer = 1;
if (bfin_library_id_string)
{
int id;
if (! TARGET_ID_SHARED_LIBRARY)
error ("-mshared-library-id= specified without -mid-shared-library");
id = atoi (bfin_library_id_string);
if (id < 0 || id > MAX_LIBRARY_ID)
error ("-mshared-library-id=%d is not between 0 and %d", id, MAX_LIBRARY_ID);
asprintf ((char **)&bfin_library_id_string, "%d", (id * -4) - 4);
}
if (TARGET_ID_SHARED_LIBRARY)
flag_pic = 1;
flag_schedule_insns = 0;
}
static int
branch_dest (rtx branch)
{
rtx dest;
int dest_uid;
rtx pat = PATTERN (branch);
if (GET_CODE (pat) == PARALLEL)
pat = XVECEXP (pat, 0, 0);
dest = SET_SRC (pat);
if (GET_CODE (dest) == IF_THEN_ELSE)
dest = XEXP (dest, 1);
dest = XEXP (dest, 0);
dest_uid = INSN_UID (dest);
return INSN_ADDRESSES (dest_uid);
}
static int
cbranch_predicted_taken_p (rtx insn)
{
rtx x = find_reg_note (insn, REG_BR_PROB, 0);
if (x)
{
int pred_val = INTVAL (XEXP (x, 0));
return pred_val >= REG_BR_PROB_BASE / 2;
}
return 0;
}
static const char *ccbranch_templates[][3] = {
{ "if !cc jump %3;", "if cc jump 4 (bp); jump.s %3;", "if cc jump 6 (bp); jump.l %3;" },
{ "if cc jump %3;", "if !cc jump 4 (bp); jump.s %3;", "if !cc jump 6 (bp); jump.l %3;" },
{ "if !cc jump %3 (bp);", "if cc jump 4; jump.s %3;", "if cc jump 6; jump.l %3;" },
{ "if cc jump %3 (bp);", "if !cc jump 4; jump.s %3;", "if !cc jump 6; jump.l %3;" },
};
void
asm_conditional_branch (rtx insn, rtx *operands, int n_nops, int predict_taken)
{
int offset = branch_dest (insn) - INSN_ADDRESSES (INSN_UID (insn));
int len = (offset >= -1024 && offset <= 1022 ? 0
: offset >= -4094 && offset <= 4096 ? 1
: 2);
int bp = predict_taken && len == 0 ? 1 : cbranch_predicted_taken_p (insn);
int idx = (bp << 1) | (GET_CODE (operands[0]) == EQ ? BRF : BRT);
output_asm_insn (ccbranch_templates[idx][len], operands);
if (n_nops > 0 && bp)
abort ();
if (len == 0)
while (n_nops-- > 0)
output_asm_insn ("nop;", NULL);
}
rtx
bfin_gen_compare (rtx cmp, enum machine_mode mode ATTRIBUTE_UNUSED)
{
enum rtx_code code1, code2;
rtx op0 = bfin_compare_op0, op1 = bfin_compare_op1;
rtx tem = bfin_cc_rtx;
enum rtx_code code = GET_CODE (cmp);
if (GET_MODE (op0) == BImode)
{
if ((code == NE || code == EQ) && op1 == const0_rtx)
tem = op0, code2 = code;
else
abort ();
}
else
{
switch (code) {
case EQ:
case LT:
case LE:
case LEU:
case LTU:
code1 = code;
code2 = NE;
break;
default:
code1 = reverse_condition (code);
code2 = EQ;
break;
}
emit_insn (gen_rtx_SET (BImode, tem,
gen_rtx_fmt_ee (code1, BImode, op0, op1)));
}
return gen_rtx_fmt_ee (code2, BImode, tem, CONST0_RTX (BImode));
}
int
log2constp (unsigned HOST_WIDE_INT c)
{
c &= 0xFFFFFFFF;
return c != 0 && (c & (c-1)) == 0;
}
static int
shiftr_zero (HOST_WIDE_INT *v)
{
unsigned HOST_WIDE_INT tmp = *v;
unsigned HOST_WIDE_INT sgn;
int n = 0;
if (tmp == 0)
return 0;
sgn = tmp & ((unsigned HOST_WIDE_INT) 1 << (HOST_BITS_PER_WIDE_INT - 1));
while ((tmp & 0x1) == 0 && n <= 32)
{
tmp = (tmp >> 1) | sgn;
n++;
}
*v = tmp;
return n;
}
int
split_load_immediate (rtx operands[])
{
HOST_WIDE_INT val = INTVAL (operands[1]);
HOST_WIDE_INT tmp;
HOST_WIDE_INT shifted = val;
HOST_WIDE_INT shifted_compl = ~val;
int num_zero = shiftr_zero (&shifted);
int num_compl_zero = shiftr_zero (&shifted_compl);
unsigned int regno = REGNO (operands[0]);
enum reg_class class1 = REGNO_REG_CLASS (regno);
if (num_zero
&& shifted >= -32768 && shifted < 65536
&& (D_REGNO_P (regno)
|| (regno >= REG_P0 && regno <= REG_P7 && num_zero <= 2)))
{
emit_insn (gen_movsi (operands[0], GEN_INT (shifted)));
emit_insn (gen_ashlsi3 (operands[0], operands[0], GEN_INT (num_zero)));
return 1;
}
tmp = val & 0xFFFF;
tmp |= -(tmp & 0x8000);
if (D_REGNO_P (regno))
{
if (log2constp (val & 0xFFFF0000))
{
emit_insn (gen_movsi (operands[0], GEN_INT (val & 0xFFFF)));
emit_insn (gen_iorsi3 (operands[0], operands[0], GEN_INT (val & 0xFFFF0000)));
return 1;
}
else if (log2constp (val | 0xFFFF) && (val & 0x8000) != 0)
{
emit_insn (gen_movsi (operands[0], GEN_INT (tmp)));
emit_insn (gen_andsi3 (operands[0], operands[0], GEN_INT (val | 0xFFFF)));
}
}
if (D_REGNO_P (regno))
{
if (CONST_7BIT_IMM_P (tmp))
{
emit_insn (gen_movsi (operands[0], GEN_INT (tmp)));
emit_insn (gen_movstricthi_high (operands[0], GEN_INT (val & -65536)));
return 1;
}
if ((val & 0xFFFF0000) == 0)
{
emit_insn (gen_movsi (operands[0], const0_rtx));
emit_insn (gen_movsi_low (operands[0], operands[0], operands[1]));
return 1;
}
if ((val & 0xFFFF0000) == 0xFFFF0000)
{
emit_insn (gen_movsi (operands[0], constm1_rtx));
emit_insn (gen_movsi_low (operands[0], operands[0], operands[1]));
return 1;
}
}
if (regno > REG_R7)
return 0;
if (optimize_size
&& num_compl_zero && CONST_7BIT_IMM_P (shifted_compl))
{
emit_insn (gen_movsi (operands[0], GEN_INT (shifted_compl)));
emit_insn (gen_ashlsi3 (operands[0], operands[0],
GEN_INT (num_compl_zero)));
emit_insn (gen_one_cmplsi2 (operands[0], operands[0]));
return 1;
}
return 0;
}
static bool
bfin_valid_add (enum machine_mode mode, HOST_WIDE_INT value)
{
unsigned HOST_WIDE_INT v = value > 0 ? value : -value;
int sz = GET_MODE_SIZE (mode);
int shift = sz == 1 ? 0 : sz == 2 ? 1 : 2;
unsigned HOST_WIDE_INT mask = sz == 8 ? 0x7ffe : 0x7fff;
return (v & ~(mask << shift)) == 0;
}
static bool
bfin_valid_reg_p (unsigned int regno, int strict)
{
return ((strict && REGNO_OK_FOR_BASE_STRICT_P (regno))
|| (!strict && REGNO_OK_FOR_BASE_NONSTRICT_P (regno)));
}
bool
bfin_legitimate_address_p (enum machine_mode mode, rtx x, int strict)
{
switch (GET_CODE (x)) {
case REG:
if (bfin_valid_reg_p (REGNO (x), strict))
return true;
break;
case PLUS:
if (REG_P (XEXP (x, 0))
&& bfin_valid_reg_p (REGNO (XEXP (x, 0)), strict)
&& (GET_CODE (XEXP (x, 1)) == UNSPEC
|| (GET_CODE (XEXP (x, 1)) == CONST_INT
&& bfin_valid_add (mode, INTVAL (XEXP (x, 1))))))
return true;
break;
case POST_INC:
case POST_DEC:
if (LEGITIMATE_MODE_FOR_AUTOINC_P (mode)
&& REG_P (XEXP (x, 0))
&& bfin_valid_reg_p (REGNO (XEXP (x, 0)), strict))
return true;
case PRE_DEC:
if (LEGITIMATE_MODE_FOR_AUTOINC_P (mode)
&& XEXP (x, 0) == stack_pointer_rtx
&& REG_P (XEXP (x, 0))
&& bfin_valid_reg_p (REGNO (XEXP (x, 0)), strict))
return true;
break;
default:
break;
}
return false;
}
static bool
bfin_rtx_costs (rtx x, int code, int outer_code, int *total)
{
int cost2 = COSTS_N_INSNS (1);
switch (code)
{
case CONST_INT:
if (outer_code == SET || outer_code == PLUS)
*total = CONST_7BIT_IMM_P (INTVAL (x)) ? 0 : cost2;
else if (outer_code == AND)
*total = log2constp (~INTVAL (x)) ? 0 : cost2;
else if (outer_code == LE || outer_code == LT || outer_code == EQ)
*total = (INTVAL (x) >= -4 && INTVAL (x) <= 3) ? 0 : cost2;
else if (outer_code == LEU || outer_code == LTU)
*total = (INTVAL (x) >= 0 && INTVAL (x) <= 7) ? 0 : cost2;
else if (outer_code == MULT)
*total = (INTVAL (x) == 2 || INTVAL (x) == 4) ? 0 : cost2;
else if (outer_code == ASHIFT && (INTVAL (x) == 1 || INTVAL (x) == 2))
*total = 0;
else if (outer_code == ASHIFT || outer_code == ASHIFTRT
|| outer_code == LSHIFTRT)
*total = (INTVAL (x) >= 0 && INTVAL (x) <= 31) ? 0 : cost2;
else if (outer_code == IOR || outer_code == XOR)
*total = (INTVAL (x) & (INTVAL (x) - 1)) == 0 ? 0 : cost2;
else
*total = cost2;
return true;
case CONST:
case LABEL_REF:
case SYMBOL_REF:
case CONST_DOUBLE:
*total = COSTS_N_INSNS (2);
return true;
case PLUS:
if (GET_MODE (x) == Pmode)
{
if (GET_CODE (XEXP (x, 0)) == MULT
&& GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT)
{
HOST_WIDE_INT val = INTVAL (XEXP (XEXP (x, 0), 1));
if (val == 2 || val == 4)
{
*total = cost2;
*total += rtx_cost (XEXP (XEXP (x, 0), 0), outer_code);
*total += rtx_cost (XEXP (x, 1), outer_code);
return true;
}
}
}
case MINUS:
case ASHIFT:
case ASHIFTRT:
case LSHIFTRT:
if (GET_MODE (x) == DImode)
*total = 6 * cost2;
return false;
case AND:
case IOR:
case XOR:
if (GET_MODE (x) == DImode)
*total = 2 * cost2;
return false;
case MULT:
if (GET_MODE_SIZE (GET_MODE (x)) <= UNITS_PER_WORD)
*total = COSTS_N_INSNS (3);
return false;
default:
return false;
}
}
static void
bfin_internal_label (FILE *stream, const char *prefix, unsigned long num)
{
fprintf (stream, "%s%s$%ld:\n", LOCAL_LABEL_PREFIX, prefix, num);
}
static int first_preg_to_save, first_dreg_to_save;
int
push_multiple_operation (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
{
int lastdreg = 8, lastpreg = 6;
int i, group;
first_preg_to_save = lastpreg;
first_dreg_to_save = lastdreg;
for (i = 1, group = 0; i < XVECLEN (op, 0) - 1; i++)
{
rtx t = XVECEXP (op, 0, i);
rtx src, dest;
int regno;
if (GET_CODE (t) != SET)
return 0;
src = SET_SRC (t);
dest = SET_DEST (t);
if (GET_CODE (dest) != MEM || ! REG_P (src))
return 0;
dest = XEXP (dest, 0);
if (GET_CODE (dest) != PLUS
|| ! REG_P (XEXP (dest, 0))
|| REGNO (XEXP (dest, 0)) != REG_SP
|| GET_CODE (XEXP (dest, 1)) != CONST_INT
|| INTVAL (XEXP (dest, 1)) != -i * 4)
return 0;
regno = REGNO (src);
if (group == 0)
{
if (D_REGNO_P (regno))
{
group = 1;
first_dreg_to_save = lastdreg = regno - REG_R0;
}
else if (regno >= REG_P0 && regno <= REG_P7)
{
group = 2;
first_preg_to_save = lastpreg = regno - REG_P0;
}
else
return 0;
continue;
}
if (group == 1)
{
if (regno >= REG_P0 && regno <= REG_P7)
{
group = 2;
first_preg_to_save = lastpreg = regno - REG_P0;
}
else if (regno != REG_R0 + lastdreg + 1)
return 0;
else
lastdreg++;
}
else if (group == 2)
{
if (regno != REG_P0 + lastpreg + 1)
return 0;
lastpreg++;
}
}
return 1;
}
int
pop_multiple_operation (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
{
int lastdreg = 8, lastpreg = 6;
int i, group;
for (i = 1, group = 0; i < XVECLEN (op, 0); i++)
{
rtx t = XVECEXP (op, 0, i);
rtx src, dest;
int regno;
if (GET_CODE (t) != SET)
return 0;
src = SET_SRC (t);
dest = SET_DEST (t);
if (GET_CODE (src) != MEM || ! REG_P (dest))
return 0;
src = XEXP (src, 0);
if (i == 1)
{
if (! REG_P (src) || REGNO (src) != REG_SP)
return 0;
}
else if (GET_CODE (src) != PLUS
|| ! REG_P (XEXP (src, 0))
|| REGNO (XEXP (src, 0)) != REG_SP
|| GET_CODE (XEXP (src, 1)) != CONST_INT
|| INTVAL (XEXP (src, 1)) != (i - 1) * 4)
return 0;
regno = REGNO (dest);
if (group == 0)
{
if (regno == REG_R7)
{
group = 1;
lastdreg = 7;
}
else if (regno != REG_P0 + lastpreg - 1)
return 0;
else
lastpreg--;
}
else if (group == 1)
{
if (regno != REG_R0 + lastdreg - 1)
return 0;
else
lastdreg--;
}
}
first_dreg_to_save = lastdreg;
first_preg_to_save = lastpreg;
return 1;
}
void
output_push_multiple (rtx insn, rtx *operands)
{
char buf[80];
if (! push_multiple_operation (PATTERN (insn), VOIDmode))
abort ();
if (first_dreg_to_save == 8)
sprintf (buf, "[--sp] = ( p5:%d );\n", first_preg_to_save);
else if (first_preg_to_save == 6)
sprintf (buf, "[--sp] = ( r7:%d );\n", first_dreg_to_save);
else
sprintf (buf, "[--sp] = ( r7:%d, p5:%d );\n", first_dreg_to_save, first_preg_to_save);
output_asm_insn (buf, operands);
}
void
output_pop_multiple (rtx insn, rtx *operands)
{
char buf[80];
if (! pop_multiple_operation (PATTERN (insn), VOIDmode))
abort ();
if (first_dreg_to_save == 8)
sprintf (buf, "( p5:%d ) = [sp++];\n", first_preg_to_save);
else if (first_preg_to_save == 6)
sprintf (buf, "( r7:%d ) = [sp++];\n", first_dreg_to_save);
else
sprintf (buf, "( r7:%d, p5:%d ) = [sp++];\n", first_dreg_to_save, first_preg_to_save);
output_asm_insn (buf, operands);
}
static void
single_move_for_strmov (rtx dst, rtx src, enum machine_mode mode, HOST_WIDE_INT offset)
{
rtx scratch = gen_reg_rtx (mode);
rtx srcmem, dstmem;
srcmem = adjust_address_nv (src, mode, offset);
dstmem = adjust_address_nv (dst, mode, offset);
emit_move_insn (scratch, srcmem);
emit_move_insn (dstmem, scratch);
}
bool
bfin_expand_strmov (rtx dst, rtx src, rtx count_exp, rtx align_exp)
{
rtx srcreg, destreg, countreg;
HOST_WIDE_INT align = 0;
unsigned HOST_WIDE_INT count = 0;
if (GET_CODE (align_exp) == CONST_INT)
align = INTVAL (align_exp);
if (GET_CODE (count_exp) == CONST_INT)
{
count = INTVAL (count_exp);
#if 0
if (!TARGET_INLINE_ALL_STRINGOPS && count > 64)
return false;
#endif
}
if (optimize_size)
{
if (count == 2 && align < 2)
return false;
if (count == 4 && align < 4)
return false;
if (count != 1 && count != 2 && count != 4)
return false;
}
if (align < 2 && count != 1)
return false;
destreg = copy_to_mode_reg (Pmode, XEXP (dst, 0));
if (destreg != XEXP (dst, 0))
dst = replace_equiv_address_nv (dst, destreg);
srcreg = copy_to_mode_reg (Pmode, XEXP (src, 0));
if (srcreg != XEXP (src, 0))
src = replace_equiv_address_nv (src, srcreg);
if (count != 0 && align >= 2)
{
unsigned HOST_WIDE_INT offset = 0;
if (align >= 4)
{
if ((count & ~3) == 4)
{
single_move_for_strmov (dst, src, SImode, offset);
offset = 4;
}
else if (count & ~3)
{
HOST_WIDE_INT new_count = ((count >> 2) & 0x3fffffff) - 1;
countreg = copy_to_mode_reg (Pmode, GEN_INT (new_count));
emit_insn (gen_rep_movsi (destreg, srcreg, countreg, destreg, srcreg));
}
}
else
{
if ((count & ~1) == 2)
{
single_move_for_strmov (dst, src, HImode, offset);
offset = 2;
}
else if (count & ~1)
{
HOST_WIDE_INT new_count = ((count >> 1) & 0x7fffffff) - 1;
countreg = copy_to_mode_reg (Pmode, GEN_INT (new_count));
emit_insn (gen_rep_movhi (destreg, srcreg, countreg, destreg, srcreg));
}
}
if (count & 2)
{
single_move_for_strmov (dst, src, HImode, offset);
offset += 2;
}
if (count & 1)
{
single_move_for_strmov (dst, src, QImode, offset);
}
return true;
}
return false;
}
static int
bfin_adjust_cost (rtx insn, rtx link, rtx dep_insn, int cost)
{
enum attr_type insn_type, dep_insn_type;
int dep_insn_code_number;
if (REG_NOTE_KIND (link) != 0)
return 0;
dep_insn_code_number = recog_memoized (dep_insn);
if (dep_insn_code_number < 0 || recog_memoized (insn) < 0)
return cost;
insn_type = get_attr_type (insn);
dep_insn_type = get_attr_type (dep_insn);
if (dep_insn_type == TYPE_MOVE || dep_insn_type == TYPE_MCLD)
{
rtx pat = PATTERN (dep_insn);
rtx dest = SET_DEST (pat);
rtx src = SET_SRC (pat);
if (! ADDRESS_REGNO_P (REGNO (dest)) || ! D_REGNO_P (REGNO (src)))
return cost;
return cost + (dep_insn_type == TYPE_MOVE ? 4 : 3);
}
return cost;
}
static void
bfin_reorg (void)
{
rtx insn, last_condjump = NULL_RTX;
int cycles_since_jump = INT_MAX;
if (! TARGET_CSYNC)
return;
for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
{
rtx pat;
if (NOTE_P (insn) || BARRIER_P (insn) || LABEL_P (insn))
continue;
pat = PATTERN (insn);
if (GET_CODE (pat) == USE || GET_CODE (pat) == CLOBBER
|| GET_CODE (pat) == ASM_INPUT || GET_CODE (pat) == ADDR_VEC
|| GET_CODE (pat) == ADDR_DIFF_VEC || asm_noperands (pat) >= 0)
continue;
if (JUMP_P (insn))
{
if (any_condjump_p (insn)
&& ! cbranch_predicted_taken_p (insn))
{
last_condjump = insn;
cycles_since_jump = 0;
}
else
cycles_since_jump = INT_MAX;
}
else if (INSN_P (insn))
{
enum attr_type type = get_attr_type (insn);
if (cycles_since_jump < INT_MAX)
cycles_since_jump++;
if (type == TYPE_MCLD && cycles_since_jump < 3)
{
rtx pat;
pat = single_set (insn);
if (may_trap_p (SET_SRC (pat)))
{
int num_clobbers;
rtx *op = recog_data.operand;
extract_insn (last_condjump);
if (optimize_size)
pat = gen_cbranch_predicted_taken (op[0], op[1], op[2],
op[3]);
else
pat = gen_cbranch_with_nops (op[0], op[1], op[2], op[3],
GEN_INT (3 - cycles_since_jump));
PATTERN (last_condjump) = pat;
INSN_CODE (last_condjump) = recog (pat, insn, &num_clobbers);
cycles_since_jump = INT_MAX;
}
}
}
}
}
static tree
handle_int_attribute (tree *node, tree name,
tree args ATTRIBUTE_UNUSED,
int flags ATTRIBUTE_UNUSED,
bool *no_add_attrs)
{
tree x = *node;
if (TREE_CODE (x) == FUNCTION_DECL)
x = TREE_TYPE (x);
if (TREE_CODE (x) != FUNCTION_TYPE)
{
warning ("%qs attribute only applies to functions",
IDENTIFIER_POINTER (name));
*no_add_attrs = true;
}
else if (funkind (x) != SUBROUTINE)
error ("multiple function type attributes specified");
return NULL_TREE;
}
static int
bfin_comp_type_attributes (tree type1, tree type2)
{
e_funkind kind1, kind2;
if (TREE_CODE (type1) != FUNCTION_TYPE)
return 1;
kind1 = funkind (type1);
kind2 = funkind (type2);
if (kind1 != kind2)
return 0;
if (!lookup_attribute ("nesting", TYPE_ATTRIBUTES (type1))
!= !lookup_attribute ("nesting", TYPE_ATTRIBUTES (type2)))
return 0;
if (!lookup_attribute ("saveall", TYPE_ATTRIBUTES (type1))
!= !lookup_attribute ("saveall", TYPE_ATTRIBUTES (type2)))
return 0;
if (!lookup_attribute ("kspisusp", TYPE_ATTRIBUTES (type1))
!= !lookup_attribute ("kspisusp", TYPE_ATTRIBUTES (type2)))
return 0;
return 1;
}
const struct attribute_spec bfin_attribute_table[] =
{
{ "interrupt_handler", 0, 0, false, true, true, handle_int_attribute },
{ "exception_handler", 0, 0, false, true, true, handle_int_attribute },
{ "nmi_handler", 0, 0, false, true, true, handle_int_attribute },
{ "nesting", 0, 0, false, true, true, NULL },
{ "kspisusp", 0, 0, false, true, true, NULL },
{ "saveall", 0, 0, false, true, true, NULL },
{ NULL, 0, 0, false, false, false, NULL }
};
static void
bfin_output_mi_thunk (FILE *file ATTRIBUTE_UNUSED,
tree thunk ATTRIBUTE_UNUSED, HOST_WIDE_INT delta,
HOST_WIDE_INT vcall_offset, tree function)
{
rtx xops[3];
rtx this = gen_rtx_REG (Pmode, REG_R0);
if (delta)
{
xops[1] = this;
if (delta >= -64 && delta <= 63)
{
xops[0] = GEN_INT (delta);
output_asm_insn ("%1 += %0;", xops);
}
else if (delta >= -128 && delta < -64)
{
xops[0] = GEN_INT (delta + 64);
output_asm_insn ("%1 += -64; %1 += %0;", xops);
}
else if (delta > 63 && delta <= 126)
{
xops[0] = GEN_INT (delta - 63);
output_asm_insn ("%1 += 63; %1 += %0;", xops);
}
else
{
xops[0] = GEN_INT (delta);
output_asm_insn ("r3.l = %h0; r3.h = %d0; %1 = %1 + r3;", xops);
}
}
if (vcall_offset)
{
rtx p2tmp = gen_rtx_REG (Pmode, REG_P2);
rtx tmp = gen_rtx_REG (Pmode, REG_R2);
xops[1] = tmp;
xops[2] = p2tmp;
output_asm_insn ("%2 = r0; %2 = [%2];", xops);
xops[0] = gen_rtx_MEM (Pmode, plus_constant (p2tmp, vcall_offset));
if (!memory_operand (xops[0], Pmode))
{
rtx tmp2 = gen_rtx_REG (Pmode, REG_P1);
xops[0] = GEN_INT (vcall_offset);
xops[1] = tmp2;
output_asm_insn ("%h1 = %h0; %d1 = %d0; %2 = %2 + %1", xops);
xops[0] = gen_rtx_MEM (Pmode, p2tmp);
}
xops[2] = this;
output_asm_insn ("%1 = %0; %2 = %2 + %1;", xops);
}
xops[0] = XEXP (DECL_RTL (function), 0);
if (1 || !flag_pic || (*targetm.binds_local_p) (function))
output_asm_insn ("jump.l\t%P0", xops);
}
#undef TARGET_ASM_GLOBALIZE_LABEL
#define TARGET_ASM_GLOBALIZE_LABEL bfin_globalize_label
#undef TARGET_ASM_FILE_START
#define TARGET_ASM_FILE_START output_file_start
#undef TARGET_ATTRIBUTE_TABLE
#define TARGET_ATTRIBUTE_TABLE bfin_attribute_table
#undef TARGET_COMP_TYPE_ATTRIBUTES
#define TARGET_COMP_TYPE_ATTRIBUTES bfin_comp_type_attributes
#undef TARGET_RTX_COSTS
#define TARGET_RTX_COSTS bfin_rtx_costs
#undef TARGET_ADDRESS_COST
#define TARGET_ADDRESS_COST bfin_address_cost
#undef TARGET_ASM_INTERNAL_LABEL
#define TARGET_ASM_INTERNAL_LABEL bfin_internal_label
#undef TARGET_MACHINE_DEPENDENT_REORG
#define TARGET_MACHINE_DEPENDENT_REORG bfin_reorg
#undef TARGET_FUNCTION_OK_FOR_SIBCALL
#define TARGET_FUNCTION_OK_FOR_SIBCALL bfin_function_ok_for_sibcall
#undef TARGET_ASM_OUTPUT_MI_THUNK
#define TARGET_ASM_OUTPUT_MI_THUNK bfin_output_mi_thunk
#undef TARGET_ASM_CAN_OUTPUT_MI_THUNK
#define TARGET_ASM_CAN_OUTPUT_MI_THUNK hook_bool_tree_hwi_hwi_tree_true
#undef TARGET_SCHED_ADJUST_COST
#define TARGET_SCHED_ADJUST_COST bfin_adjust_cost
#undef TARGET_PROMOTE_PROTOTYPES
#define TARGET_PROMOTE_PROTOTYPES hook_bool_tree_true
#undef TARGET_PROMOTE_FUNCTION_ARGS
#define TARGET_PROMOTE_FUNCTION_ARGS hook_bool_tree_true
#undef TARGET_PROMOTE_FUNCTION_RETURN
#define TARGET_PROMOTE_FUNCTION_RETURN hook_bool_tree_true
#undef TARGET_ARG_PARTIAL_BYTES
#define TARGET_ARG_PARTIAL_BYTES bfin_arg_partial_bytes
#undef TARGET_PASS_BY_REFERENCE
#define TARGET_PASS_BY_REFERENCE bfin_pass_by_reference
#undef TARGET_SETUP_INCOMING_VARARGS
#define TARGET_SETUP_INCOMING_VARARGS setup_incoming_varargs
#undef TARGET_STRUCT_VALUE_RTX
#define TARGET_STRUCT_VALUE_RTX bfin_struct_value_rtx
#undef TARGET_VECTOR_MODE_SUPPORTED_P
#define TARGET_VECTOR_MODE_SUPPORTED_P bfin_vector_mode_supported_p
struct gcc_target targetm = TARGET_INITIALIZER;