#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "tm.h"
#include "rtl.h"
#include "tree.h"
#include "regs.h"
#include "hard-reg-set.h"
#include "real.h"
#include "insn-config.h"
#include "conditions.h"
#include "output.h"
#include "insn-attr.h"
#include "flags.h"
#include "recog.h"
#include "expr.h"
#include "optabs.h"
#include "function.h"
#include "obstack.h"
#include "toplev.h"
#include "tm_p.h"
#include "target.h"
#include "target-def.h"
int mn10300_unspec_int_label_counter;
int mn10300_protect_label;
#define REG_SAVE_BYTES (4 * regs_ever_live[2] \
+ 4 * regs_ever_live[3] \
+ 4 * regs_ever_live[6] \
+ 4 * regs_ever_live[7] \
+ 16 * (regs_ever_live[14] || regs_ever_live[15] \
|| regs_ever_live[16] || regs_ever_live[17]))
static int mn10300_address_cost_1 (rtx, int *);
static int mn10300_address_cost (rtx);
static bool mn10300_rtx_costs (rtx, int, int, int *);
static void mn10300_file_start (void);
static bool mn10300_return_in_memory (tree, tree);
static rtx mn10300_builtin_saveregs (void);
static bool mn10300_pass_by_reference (CUMULATIVE_ARGS *, enum machine_mode,
tree, bool);
static int mn10300_arg_partial_bytes (CUMULATIVE_ARGS *, enum machine_mode,
tree, bool);
#undef TARGET_ASM_ALIGNED_HI_OP
#define TARGET_ASM_ALIGNED_HI_OP "\t.hword\t"
#undef TARGET_RTX_COSTS
#define TARGET_RTX_COSTS mn10300_rtx_costs
#undef TARGET_ADDRESS_COST
#define TARGET_ADDRESS_COST mn10300_address_cost
#undef TARGET_ASM_FILE_START
#define TARGET_ASM_FILE_START mn10300_file_start
#undef TARGET_ASM_FILE_START_FILE_DIRECTIVE
#define TARGET_ASM_FILE_START_FILE_DIRECTIVE true
#undef TARGET_ENCODE_SECTION_INFO
#define TARGET_ENCODE_SECTION_INFO mn10300_encode_section_info
#undef TARGET_PROMOTE_PROTOTYPES
#define TARGET_PROMOTE_PROTOTYPES hook_bool_tree_true
#undef TARGET_RETURN_IN_MEMORY
#define TARGET_RETURN_IN_MEMORY mn10300_return_in_memory
#undef TARGET_PASS_BY_REFERENCE
#define TARGET_PASS_BY_REFERENCE mn10300_pass_by_reference
#undef TARGET_CALLEE_COPIES
#define TARGET_CALLEE_COPIES hook_bool_CUMULATIVE_ARGS_mode_tree_bool_true
#undef TARGET_ARG_PARTIAL_BYTES
#define TARGET_ARG_PARTIAL_BYTES mn10300_arg_partial_bytes
#undef TARGET_EXPAND_BUILTIN_SAVEREGS
#define TARGET_EXPAND_BUILTIN_SAVEREGS mn10300_builtin_saveregs
static void mn10300_encode_section_info (tree, rtx, int);
struct gcc_target targetm = TARGET_INITIALIZER;
static void
mn10300_file_start (void)
{
default_file_start ();
if (TARGET_AM33_2)
fprintf (asm_out_file, "\t.am33_2\n");
else if (TARGET_AM33)
fprintf (asm_out_file, "\t.am33\n");
}
void
print_operand (FILE *file, rtx x, int code)
{
switch (code)
{
case 'b':
case 'B':
if (cc_status.mdep.fpCC)
{
switch (code == 'b' ? GET_CODE (x)
: reverse_condition_maybe_unordered (GET_CODE (x)))
{
case NE:
fprintf (file, "ne");
break;
case EQ:
fprintf (file, "eq");
break;
case GE:
fprintf (file, "ge");
break;
case GT:
fprintf (file, "gt");
break;
case LE:
fprintf (file, "le");
break;
case LT:
fprintf (file, "lt");
break;
case ORDERED:
fprintf (file, "lge");
break;
case UNORDERED:
fprintf (file, "uo");
break;
case LTGT:
fprintf (file, "lg");
break;
case UNEQ:
fprintf (file, "ue");
break;
case UNGE:
fprintf (file, "uge");
break;
case UNGT:
fprintf (file, "ug");
break;
case UNLE:
fprintf (file, "ule");
break;
case UNLT:
fprintf (file, "ul");
break;
default:
abort ();
}
break;
}
switch (code == 'b' ? GET_CODE (x) : reverse_condition (GET_CODE (x)))
{
case NE:
fprintf (file, "ne");
break;
case EQ:
fprintf (file, "eq");
break;
case GE:
fprintf (file, "ge");
break;
case GT:
fprintf (file, "gt");
break;
case LE:
fprintf (file, "le");
break;
case LT:
fprintf (file, "lt");
break;
case GEU:
fprintf (file, "cc");
break;
case GTU:
fprintf (file, "hi");
break;
case LEU:
fprintf (file, "ls");
break;
case LTU:
fprintf (file, "cs");
break;
default:
abort ();
}
break;
case 'C':
if (GET_CODE (x) == REG)
{
fputc ('(', file);
print_operand (file, x, 0);
fputc (')', file);
}
else
print_operand (file, x, 0);
break;
case 'D':
switch (GET_CODE (x))
{
case MEM:
fputc ('(', file);
output_address (XEXP (x, 0));
fputc (')', file);
break;
case REG:
fprintf (file, "fd%d", REGNO (x) - 18);
break;
default:
abort ();
}
break;
case 'L':
switch (GET_CODE (x))
{
case MEM:
fputc ('(', file);
output_address (XEXP (x, 0));
fputc (')', file);
break;
case REG:
fprintf (file, "%s", reg_names[REGNO (x)]);
break;
case SUBREG:
fprintf (file, "%s", reg_names[subreg_regno (x)]);
break;
case CONST_DOUBLE:
{
long val[2];
REAL_VALUE_TYPE rv;
switch (GET_MODE (x))
{
case DFmode:
REAL_VALUE_FROM_CONST_DOUBLE (rv, x);
REAL_VALUE_TO_TARGET_DOUBLE (rv, val);
fprintf (file, "0x%lx", val[0]);
break;;
case SFmode:
REAL_VALUE_FROM_CONST_DOUBLE (rv, x);
REAL_VALUE_TO_TARGET_SINGLE (rv, val[0]);
fprintf (file, "0x%lx", val[0]);
break;;
case VOIDmode:
case DImode:
print_operand_address (file,
GEN_INT (CONST_DOUBLE_LOW (x)));
break;
default:
break;
}
break;
}
case CONST_INT:
{
rtx low, high;
split_double (x, &low, &high);
fprintf (file, "%ld", (long)INTVAL (low));
break;
}
default:
abort ();
}
break;
case 'H':
switch (GET_CODE (x))
{
case MEM:
fputc ('(', file);
x = adjust_address (x, SImode, 4);
output_address (XEXP (x, 0));
fputc (')', file);
break;
case REG:
fprintf (file, "%s", reg_names[REGNO (x) + 1]);
break;
case SUBREG:
fprintf (file, "%s", reg_names[subreg_regno (x) + 1]);
break;
case CONST_DOUBLE:
{
long val[2];
REAL_VALUE_TYPE rv;
switch (GET_MODE (x))
{
case DFmode:
REAL_VALUE_FROM_CONST_DOUBLE (rv, x);
REAL_VALUE_TO_TARGET_DOUBLE (rv, val);
fprintf (file, "0x%lx", val[1]);
break;;
case SFmode:
abort ();
case VOIDmode:
case DImode:
print_operand_address (file,
GEN_INT (CONST_DOUBLE_HIGH (x)));
break;
default:
break;
}
break;
}
case CONST_INT:
{
rtx low, high;
split_double (x, &low, &high);
fprintf (file, "%ld", (long)INTVAL (high));
break;
}
default:
abort ();
}
break;
case 'A':
fputc ('(', file);
if (GET_CODE (XEXP (x, 0)) == REG)
output_address (gen_rtx_PLUS (SImode, XEXP (x, 0), const0_rtx));
else
output_address (XEXP (x, 0));
fputc (')', file);
break;
case 'N':
if (INTVAL (x) < -128 || INTVAL (x) > 255)
abort ();
fprintf (file, "%d", (int)((~INTVAL (x)) & 0xff));
break;
case 'U':
if (INTVAL (x) < -128 || INTVAL (x) > 255)
abort ();
fprintf (file, "%d", (int)(INTVAL (x) & 0xff));
break;
case 'S':
if (GET_CODE (x) == CONST_INT)
{
fprintf (file, "%d", (int)(INTVAL (x) & 0x1f));
break;
}
default:
switch (GET_CODE (x))
{
case MEM:
fputc ('(', file);
output_address (XEXP (x, 0));
fputc (')', file);
break;
case PLUS:
output_address (x);
break;
case REG:
fprintf (file, "%s", reg_names[REGNO (x)]);
break;
case SUBREG:
fprintf (file, "%s", reg_names[subreg_regno (x)]);
break;
case CONST_DOUBLE:
{
unsigned long val;
REAL_VALUE_TYPE rv;
REAL_VALUE_FROM_CONST_DOUBLE (rv, x);
REAL_VALUE_TO_TARGET_SINGLE (rv, val);
fprintf (file, "0x%lx", val);
break;
}
case CONST_INT:
case SYMBOL_REF:
case CONST:
case LABEL_REF:
case CODE_LABEL:
case UNSPEC:
print_operand_address (file, x);
break;
default:
abort ();
}
break;
}
}
void
print_operand_address (FILE *file, rtx addr)
{
switch (GET_CODE (addr))
{
case POST_INC:
print_operand_address (file, XEXP (addr, 0));
fputc ('+', file);
break;
case REG:
print_operand (file, addr, 0);
break;
case PLUS:
{
rtx base, index;
if (REG_P (XEXP (addr, 0))
&& REG_OK_FOR_BASE_P (XEXP (addr, 0)))
base = XEXP (addr, 0), index = XEXP (addr, 1);
else if (REG_P (XEXP (addr, 1))
&& REG_OK_FOR_BASE_P (XEXP (addr, 1)))
base = XEXP (addr, 1), index = XEXP (addr, 0);
else
abort ();
print_operand (file, index, 0);
fputc (',', file);
print_operand (file, base, 0);;
break;
}
case SYMBOL_REF:
output_addr_const (file, addr);
break;
default:
output_addr_const (file, addr);
break;
}
}
static int
fp_regs_to_save (void)
{
int i, n = 0;
if (! TARGET_AM33_2)
return 0;
for (i = FIRST_FP_REGNUM; i <= LAST_FP_REGNUM; ++i)
if (regs_ever_live[i] && ! call_used_regs[i])
++n;
return n;
}
void
mn10300_print_reg_list (FILE *file, int mask)
{
int need_comma;
int i;
need_comma = 0;
fputc ('[', file);
for (i = 0; i < FIRST_EXTENDED_REGNUM; i++)
if ((mask & (1 << i)) != 0)
{
if (need_comma)
fputc (',', file);
fputs (reg_names [i], file);
need_comma = 1;
}
if ((mask & 0x3c000) != 0)
{
if ((mask & 0x3c000) != 0x3c000)
abort();
if (need_comma)
fputc (',', file);
fputs ("exreg1", file);
need_comma = 1;
}
fputc (']', file);
}
int
can_use_return_insn (void)
{
int size = get_frame_size () + current_function_outgoing_args_size;
size += current_function_outgoing_args_size ? 4 : 0;
return (reload_completed
&& size == 0
&& !regs_ever_live[2]
&& !regs_ever_live[3]
&& !regs_ever_live[6]
&& !regs_ever_live[7]
&& !regs_ever_live[14]
&& !regs_ever_live[15]
&& !regs_ever_live[16]
&& !regs_ever_live[17]
&& fp_regs_to_save () == 0
&& !frame_pointer_needed);
}
int
mn10300_get_live_callee_saved_regs (void)
{
int mask;
int i;
mask = 0;
for (i = 0; i <= LAST_EXTENDED_REGNUM; i++)
if (regs_ever_live[i] && ! call_used_regs[i])
mask |= (1 << i);
if ((mask & 0x3c000) != 0)
mask |= 0x3c000;
return mask;
}
void
mn10300_gen_multiple_store (int mask)
{
if (mask != 0)
{
int i;
int count;
rtx par;
int pari;
count = 0;
for (i = 0; i <= LAST_EXTENDED_REGNUM; i++)
if ((mask & (1 << i)) != 0)
count += 1;
par = gen_rtx_PARALLEL (VOIDmode, rtvec_alloc (count + 1));
XVECEXP (par, 0, 0)
= gen_rtx_SET (SImode,
stack_pointer_rtx,
gen_rtx_PLUS (SImode,
stack_pointer_rtx,
GEN_INT (-count * 4)));
pari = 1;
for (i = LAST_EXTENDED_REGNUM; i >= 0; i--)
if ((mask & (1 << i)) != 0)
{
rtx address = gen_rtx_PLUS (SImode,
stack_pointer_rtx,
GEN_INT (-pari * 4));
XVECEXP(par, 0, pari)
= gen_rtx_SET (VOIDmode,
gen_rtx_MEM (SImode, address),
gen_rtx_REG (SImode, i));
pari += 1;
}
par = emit_insn (par);
RTX_FRAME_RELATED_P (par) = 1;
}
}
void
expand_prologue (void)
{
HOST_WIDE_INT size;
size = get_frame_size () + current_function_outgoing_args_size;
size += (current_function_outgoing_args_size ? 4 : 0);
mn10300_gen_multiple_store (mn10300_get_live_callee_saved_regs ());
if (TARGET_AM33_2 && fp_regs_to_save ())
{
int num_regs_to_save = fp_regs_to_save (), i;
HOST_WIDE_INT xsize;
enum { save_sp_merge,
save_sp_no_merge,
save_sp_partial_merge,
save_a0_merge,
save_a0_no_merge } strategy;
unsigned int strategy_size = (unsigned)-1, this_strategy_size;
rtx reg;
rtx insn;
#define SIZE_ADD_AX(S) ((((S) >= (1 << 15)) || ((S) < -(1 << 15))) ? 6 \
: (((S) >= (1 << 7)) || ((S) < -(1 << 7))) ? 4 : 2)
#define SIZE_ADD_SP(S) ((((S) >= (1 << 15)) || ((S) < -(1 << 15))) ? 6 \
: (((S) >= (1 << 7)) || ((S) < -(1 << 7))) ? 4 : 3)
#define SIZE_FMOV_LIMIT(S,N,L,SIZE1,SIZE2,ELSE) \
(((S) >= (L)) ? (SIZE1) * (N) \
: ((S) + 4 * (N) >= (L)) ? (((L) - (S)) / 4 * (SIZE2) \
+ ((S) + 4 * (N) - (L)) / 4 * (SIZE1)) \
: (ELSE))
#define SIZE_FMOV_SP_(S,N) \
(SIZE_FMOV_LIMIT ((S), (N), (1 << 24), 7, 6, \
SIZE_FMOV_LIMIT ((S), (N), (1 << 8), 6, 4, \
(S) ? 4 * (N) : 3 + 4 * ((N) - 1))))
#define SIZE_FMOV_SP(S,N) (SIZE_FMOV_SP_ ((unsigned HOST_WIDE_INT)(S), (N)))
if (! frame_pointer_needed && size)
{
this_strategy_size = SIZE_ADD_SP (-(size + 4 * num_regs_to_save));
this_strategy_size += SIZE_FMOV_SP (size, num_regs_to_save);
if (this_strategy_size < strategy_size)
{
strategy = save_sp_merge;
strategy_size = this_strategy_size;
}
}
this_strategy_size = SIZE_ADD_SP (-4 * num_regs_to_save);
this_strategy_size += SIZE_FMOV_SP (0, num_regs_to_save);
if (size)
{
this_strategy_size += SIZE_ADD_SP (-size);
}
if (this_strategy_size < strategy_size)
{
strategy = save_sp_no_merge;
strategy_size = this_strategy_size;
}
if (! frame_pointer_needed && size + 4 * num_regs_to_save > 128)
{
this_strategy_size = SIZE_ADD_SP (-128);
this_strategy_size += SIZE_FMOV_SP (128 - 4 * num_regs_to_save,
num_regs_to_save);
if (size)
{
this_strategy_size += SIZE_ADD_SP (128 - size);
}
if (this_strategy_size < strategy_size)
{
strategy = save_sp_partial_merge;
strategy_size = this_strategy_size;
}
}
if (! frame_pointer_needed && size
&& call_used_regs[FIRST_ADDRESS_REGNUM]
&& ! fixed_regs[FIRST_ADDRESS_REGNUM])
{
this_strategy_size = SIZE_ADD_SP (-(size + 4 * num_regs_to_save));
this_strategy_size++;
if (size)
{
this_strategy_size += SIZE_ADD_AX (size);
}
this_strategy_size += 3 * num_regs_to_save;
if (this_strategy_size < strategy_size)
{
strategy = save_a0_merge;
strategy_size = this_strategy_size;
}
}
if (call_used_regs[FIRST_ADDRESS_REGNUM]
&& ! fixed_regs[FIRST_ADDRESS_REGNUM])
{
this_strategy_size = SIZE_ADD_SP (-4 * num_regs_to_save);
this_strategy_size++;
this_strategy_size += 3 * num_regs_to_save;
if (size)
{
this_strategy_size += SIZE_ADD_SP (-size);
}
if (this_strategy_size < strategy_size)
{
strategy = save_a0_no_merge;
strategy_size = this_strategy_size;
}
}
switch (strategy)
{
case save_sp_no_merge:
case save_a0_no_merge:
emit_insn (gen_addsi3 (stack_pointer_rtx,
stack_pointer_rtx,
GEN_INT (-4 * num_regs_to_save)));
xsize = 0;
break;
case save_sp_partial_merge:
emit_insn (gen_addsi3 (stack_pointer_rtx,
stack_pointer_rtx,
GEN_INT (-128)));
xsize = 128 - 4 * num_regs_to_save;
size -= xsize;
break;
case save_sp_merge:
case save_a0_merge:
emit_insn (gen_addsi3 (stack_pointer_rtx,
stack_pointer_rtx,
GEN_INT (-(size + 4 * num_regs_to_save))));
xsize = size;
size = 0;
break;
default:
abort ();
}
switch (strategy)
{
case save_sp_merge:
case save_sp_no_merge:
case save_sp_partial_merge:
reg = 0;
break;
case save_a0_merge:
case save_a0_no_merge:
reg = gen_rtx_REG (SImode, FIRST_ADDRESS_REGNUM);
emit_insn (gen_movsi (reg, stack_pointer_rtx));
if (xsize)
emit_insn (gen_addsi3 (reg, reg, GEN_INT (xsize)));
reg = gen_rtx_POST_INC (SImode, reg);
break;
default:
abort ();
}
for (i = FIRST_FP_REGNUM; i <= LAST_FP_REGNUM; ++i)
if (regs_ever_live[i] && ! call_used_regs[i])
{
rtx addr;
if (reg)
addr = reg;
else
{
if (xsize)
{
addr = gen_rtx_PLUS (SImode,
stack_pointer_rtx,
GEN_INT (xsize));
}
else
addr = stack_pointer_rtx;
xsize += 4;
}
insn = emit_insn (gen_movsi (gen_rtx_MEM (SImode, addr),
gen_rtx_REG (SImode, i)));
RTX_FRAME_RELATED_P (insn) = 1;
}
}
if (frame_pointer_needed)
emit_move_insn (frame_pointer_rtx, stack_pointer_rtx);
if (size)
emit_insn (gen_addsi3 (stack_pointer_rtx,
stack_pointer_rtx,
GEN_INT (-size)));
if (flag_pic && regs_ever_live[PIC_OFFSET_TABLE_REGNUM])
{
rtx insn = get_last_insn ();
rtx last = emit_insn (gen_GOTaddr2picreg ());
do
{
insn = NEXT_INSN (insn);
REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_MAYBE_DEAD,
const0_rtx,
REG_NOTES (insn));
}
while (insn != last);
}
}
void
expand_epilogue (void)
{
HOST_WIDE_INT size;
size = get_frame_size () + current_function_outgoing_args_size;
size += (current_function_outgoing_args_size ? 4 : 0);
if (TARGET_AM33_2 && fp_regs_to_save ())
{
int num_regs_to_save = fp_regs_to_save (), i;
rtx reg = 0;
if (frame_pointer_needed)
reg = gen_rtx_REG (SImode, FRAME_POINTER_REGNUM);
else
{
enum { restore_sp_post_adjust,
restore_sp_pre_adjust,
restore_sp_partial_adjust,
restore_a1 } strategy;
unsigned int this_strategy_size, strategy_size = (unsigned)-1;
this_strategy_size = SIZE_FMOV_SP (size, num_regs_to_save);
if (size + 4 * num_regs_to_save + REG_SAVE_BYTES > 255)
{
this_strategy_size += SIZE_ADD_SP (size + 4 * num_regs_to_save);
}
if (! REG_SAVE_BYTES)
this_strategy_size--;
if (this_strategy_size < strategy_size)
{
strategy = restore_sp_post_adjust;
strategy_size = this_strategy_size;
}
this_strategy_size = SIZE_ADD_SP (size);
this_strategy_size += SIZE_FMOV_SP (0, num_regs_to_save);
if (this_strategy_size < strategy_size)
{
strategy = restore_sp_pre_adjust;
strategy_size = this_strategy_size;
}
if (size + 4 * num_regs_to_save + REG_SAVE_BYTES > 255)
{
this_strategy_size = SIZE_ADD_SP (size + 4 * num_regs_to_save
+ REG_SAVE_BYTES - 252);
this_strategy_size += SIZE_FMOV_SP (252 - REG_SAVE_BYTES
- 4 * num_regs_to_save,
num_regs_to_save);
if (this_strategy_size < strategy_size)
{
strategy = restore_sp_partial_adjust;
strategy_size = this_strategy_size;
}
}
if (call_used_regs[FIRST_ADDRESS_REGNUM+1]
&& ! fixed_regs[FIRST_ADDRESS_REGNUM+1])
{
this_strategy_size = 1;
if (size)
{
this_strategy_size += SIZE_ADD_AX (size);
}
this_strategy_size += 3 * num_regs_to_save;
if (size + 4 * num_regs_to_save + REG_SAVE_BYTES > 255)
{
this_strategy_size += 2;
}
if (! REG_SAVE_BYTES)
this_strategy_size--;
if (this_strategy_size < strategy_size)
{
strategy = restore_a1;
strategy_size = this_strategy_size;
}
}
switch (strategy)
{
case restore_sp_post_adjust:
break;
case restore_sp_pre_adjust:
emit_insn (gen_addsi3 (stack_pointer_rtx,
stack_pointer_rtx,
GEN_INT (size)));
size = 0;
break;
case restore_sp_partial_adjust:
emit_insn (gen_addsi3 (stack_pointer_rtx,
stack_pointer_rtx,
GEN_INT (size + 4 * num_regs_to_save
+ REG_SAVE_BYTES - 252)));
size = 252 - REG_SAVE_BYTES - 4 * num_regs_to_save;
break;
case restore_a1:
reg = gen_rtx_REG (SImode, FIRST_ADDRESS_REGNUM + 1);
emit_insn (gen_movsi (reg, stack_pointer_rtx));
if (size)
emit_insn (gen_addsi3 (reg, reg, GEN_INT (size)));
break;
default:
abort ();
}
}
if (reg)
reg = gen_rtx_POST_INC (SImode, reg);
for (i = FIRST_FP_REGNUM; i <= LAST_FP_REGNUM; ++i)
if (regs_ever_live[i] && ! call_used_regs[i])
{
rtx addr;
if (reg)
addr = reg;
else if (size)
{
addr = gen_rtx_PLUS (SImode,
stack_pointer_rtx,
GEN_INT (size));
}
else
addr = stack_pointer_rtx;
size += 4;
emit_insn (gen_movsi (gen_rtx_REG (SImode, i),
gen_rtx_MEM (SImode, addr)));
}
if (! frame_pointer_needed && reg && size + REG_SAVE_BYTES > 255)
{
emit_move_insn (stack_pointer_rtx, XEXP (reg, 0));
size = 0;
}
}
if (frame_pointer_needed)
{
emit_move_insn (stack_pointer_rtx, frame_pointer_rtx);
size = 0;
}
else if (size + REG_SAVE_BYTES > 255)
{
emit_insn (gen_addsi3 (stack_pointer_rtx,
stack_pointer_rtx,
GEN_INT (size)));
size = 0;
}
if (size || regs_ever_live[2] || regs_ever_live[3]
|| regs_ever_live[6] || regs_ever_live[7]
|| regs_ever_live[14] || regs_ever_live[15]
|| regs_ever_live[16] || regs_ever_live[17]
|| frame_pointer_needed)
emit_jump_insn (gen_return_internal_regs
(GEN_INT (size + REG_SAVE_BYTES)));
else
emit_jump_insn (gen_return_internal ());
}
void
notice_update_cc (rtx body, rtx insn)
{
switch (get_attr_cc (insn))
{
case CC_NONE:
break;
case CC_NONE_0HIT:
if (cc_status.value1 != 0
&& reg_overlap_mentioned_p (recog_data.operand[0], cc_status.value1))
cc_status.value1 = 0;
break;
case CC_SET_ZN:
CC_STATUS_INIT;
cc_status.flags |= CC_NO_CARRY | CC_OVERFLOW_UNUSABLE;
cc_status.value1 = recog_data.operand[0];
break;
case CC_SET_ZNV:
CC_STATUS_INIT;
cc_status.flags |= CC_NO_CARRY;
cc_status.value1 = recog_data.operand[0];
break;
case CC_COMPARE:
CC_STATUS_INIT;
cc_status.value1 = SET_SRC (body);
if (GET_CODE (cc_status.value1) == COMPARE
&& GET_MODE (XEXP (cc_status.value1, 0)) == SFmode)
cc_status.mdep.fpCC = 1;
break;
case CC_CLOBBER:
CC_STATUS_INIT;
break;
default:
abort ();
}
}
int
store_multiple_operation (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
{
int count;
int mask;
int i;
unsigned int last;
rtx elt;
count = XVECLEN (op, 0);
if (count < 2)
return 0;
elt = XVECEXP (op, 0, 0);
if (GET_CODE (elt) != SET
|| GET_CODE (SET_DEST (elt)) != REG
|| REGNO (SET_DEST (elt)) != STACK_POINTER_REGNUM
|| GET_CODE (SET_SRC (elt)) != PLUS)
return 0;
elt = SET_SRC (elt);
if (GET_CODE (XEXP (elt, 0)) != REG
|| REGNO (XEXP (elt, 0)) != STACK_POINTER_REGNUM
|| GET_CODE (XEXP (elt, 1)) != CONST_INT
|| INTVAL (XEXP (elt, 1)) != -(count - 1) * 4)
return 0;
last = LAST_EXTENDED_REGNUM + 1;
mask = 0;
for (i = 1; i < count; i++)
{
elt = XVECEXP (op, 0, i);
if (GET_CODE (elt) != SET
|| GET_CODE (SET_DEST (elt)) != MEM
|| GET_CODE (SET_SRC (elt)) != REG
|| REGNO (SET_SRC (elt)) >= last)
return 0;
last = REGNO (SET_SRC (elt));
mask |= (1 << last);
elt = XEXP (SET_DEST (elt), 0);
if (GET_CODE (elt) != PLUS
|| GET_CODE (XEXP (elt, 0)) != REG
|| REGNO (XEXP (elt, 0)) != STACK_POINTER_REGNUM
|| GET_CODE (XEXP (elt, 1)) != CONST_INT
|| INTVAL (XEXP (elt, 1)) != -i * 4)
return 0;
}
if ((mask & 0x3c000) != 0
&& (mask & 0x3c000) != 0x3c000)
return 0;
return mask;
}
int
call_address_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
{
if (flag_pic)
return (EXTRA_CONSTRAINT (op, 'S') || GET_CODE (op) == REG);
return (GET_CODE (op) == SYMBOL_REF || GET_CODE (op) == REG);
}
enum reg_class
secondary_reload_class (enum reg_class class, enum machine_mode mode, rtx in)
{
if ((GET_CODE (in) == MEM
|| (GET_CODE (in) == REG
&& REGNO (in) >= FIRST_PSEUDO_REGISTER)
|| (GET_CODE (in) == SUBREG
&& GET_CODE (SUBREG_REG (in)) == REG
&& REGNO (SUBREG_REG (in)) >= FIRST_PSEUDO_REGISTER))
&& (mode == QImode || mode == HImode)
&& (class == ADDRESS_REGS || class == SP_REGS
|| class == SP_OR_ADDRESS_REGS))
{
if (TARGET_AM33)
return DATA_OR_EXTENDED_REGS;
return DATA_REGS;
}
if (class != SP_REGS
&& class != ADDRESS_REGS
&& class != SP_OR_ADDRESS_REGS
&& class != SP_OR_EXTENDED_REGS
&& class != ADDRESS_OR_EXTENDED_REGS
&& class != SP_OR_ADDRESS_OR_EXTENDED_REGS
&& (in == stack_pointer_rtx
|| (GET_CODE (in) == PLUS
&& (XEXP (in, 0) == stack_pointer_rtx
|| XEXP (in, 1) == stack_pointer_rtx))))
return ADDRESS_REGS;
if (GET_CODE (in) == PLUS
&& (XEXP (in, 0) == stack_pointer_rtx
|| XEXP (in, 1) == stack_pointer_rtx))
{
if (TARGET_AM33)
return DATA_OR_EXTENDED_REGS;
return DATA_REGS;
}
if (TARGET_AM33_2 && class == FP_REGS
&& GET_CODE (in) == MEM && ! OK_FOR_Q (in))
{
if (TARGET_AM33)
return DATA_OR_EXTENDED_REGS;
return DATA_REGS;
}
return NO_REGS;
}
int
initial_offset (int from, int to)
{
if (from == ARG_POINTER_REGNUM && to == FRAME_POINTER_REGNUM)
{
if (regs_ever_live[2] || regs_ever_live[3]
|| regs_ever_live[6] || regs_ever_live[7]
|| regs_ever_live[14] || regs_ever_live[15]
|| regs_ever_live[16] || regs_ever_live[17]
|| fp_regs_to_save ()
|| frame_pointer_needed)
return REG_SAVE_BYTES
+ 4 * fp_regs_to_save ();
else
return 0;
}
if (from == ARG_POINTER_REGNUM && to == STACK_POINTER_REGNUM)
{
if (regs_ever_live[2] || regs_ever_live[3]
|| regs_ever_live[6] || regs_ever_live[7]
|| regs_ever_live[14] || regs_ever_live[15]
|| regs_ever_live[16] || regs_ever_live[17]
|| fp_regs_to_save ()
|| frame_pointer_needed)
return (get_frame_size () + REG_SAVE_BYTES
+ 4 * fp_regs_to_save ()
+ (current_function_outgoing_args_size
? current_function_outgoing_args_size + 4 : 0));
else
return (get_frame_size ()
+ (current_function_outgoing_args_size
? current_function_outgoing_args_size + 4 : 0));
}
if (from == FRAME_POINTER_REGNUM && to == STACK_POINTER_REGNUM)
return (get_frame_size ()
+ (current_function_outgoing_args_size
? current_function_outgoing_args_size + 4 : 0));
abort ();
}
static bool
mn10300_return_in_memory (tree type, tree fntype ATTRIBUTE_UNUSED)
{
return int_size_in_bytes (type) > 8 || TYPE_MODE (type) == BLKmode;
}
static rtx
mn10300_builtin_saveregs (void)
{
rtx offset, mem;
tree fntype = TREE_TYPE (current_function_decl);
int argadj = ((!(TYPE_ARG_TYPES (fntype) != 0
&& (TREE_VALUE (tree_last (TYPE_ARG_TYPES (fntype)))
!= void_type_node)))
? UNITS_PER_WORD : 0);
int set = get_varargs_alias_set ();
if (argadj)
offset = plus_constant (current_function_arg_offset_rtx, argadj);
else
offset = current_function_arg_offset_rtx;
mem = gen_rtx_MEM (SImode, current_function_internal_arg_pointer);
set_mem_alias_set (mem, set);
emit_move_insn (mem, gen_rtx_REG (SImode, 0));
mem = gen_rtx_MEM (SImode,
plus_constant (current_function_internal_arg_pointer, 4));
set_mem_alias_set (mem, set);
emit_move_insn (mem, gen_rtx_REG (SImode, 1));
return copy_to_reg (expand_binop (Pmode, add_optab,
current_function_internal_arg_pointer,
offset, 0, 0, OPTAB_LIB_WIDEN));
}
void
mn10300_va_start (tree valist, rtx nextarg)
{
nextarg = expand_builtin_saveregs ();
std_expand_builtin_va_start (valist, nextarg);
}
static bool
mn10300_pass_by_reference (CUMULATIVE_ARGS *cum ATTRIBUTE_UNUSED,
enum machine_mode mode, tree type,
bool named ATTRIBUTE_UNUSED)
{
unsigned HOST_WIDE_INT size;
if (type)
size = int_size_in_bytes (type);
else
size = GET_MODE_SIZE (mode);
return size > 8;
}
rtx
function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode,
tree type, int named ATTRIBUTE_UNUSED)
{
rtx result = 0;
int size, align;
int nregs = 2;
if (mode == BLKmode)
size = int_size_in_bytes (type);
else
size = GET_MODE_SIZE (mode);
align = size;
cum->nbytes = (cum->nbytes + 3) & ~3;
if (cum->nbytes > nregs * UNITS_PER_WORD)
return 0;
if (type == NULL_TREE
&& cum->nbytes + size > nregs * UNITS_PER_WORD)
return 0;
switch (cum->nbytes / UNITS_PER_WORD)
{
case 0:
result = gen_rtx_REG (mode, 0);
break;
case 1:
result = gen_rtx_REG (mode, 1);
break;
default:
result = 0;
}
return result;
}
static int
mn10300_arg_partial_bytes (CUMULATIVE_ARGS *cum, enum machine_mode mode,
tree type, bool named ATTRIBUTE_UNUSED)
{
int size, align;
int nregs = 2;
if (mode == BLKmode)
size = int_size_in_bytes (type);
else
size = GET_MODE_SIZE (mode);
align = size;
cum->nbytes = (cum->nbytes + 3) & ~3;
if (cum->nbytes > nregs * UNITS_PER_WORD)
return 0;
if (cum->nbytes + size <= nregs * UNITS_PER_WORD)
return 0;
if (type == NULL_TREE
&& cum->nbytes + size > nregs * UNITS_PER_WORD)
return 0;
return nregs * UNITS_PER_WORD - cum->nbytes;
}
const char *
output_tst (rtx operand, rtx insn)
{
rtx temp;
int past_call = 0;
temp = PREV_INSN (insn);
while (optimize && temp)
{
rtx set;
if (GET_CODE (temp) == CODE_LABEL
|| GET_CODE (temp) == JUMP_INSN
|| GET_CODE (temp) == BARRIER)
break;
if (GET_CODE (temp) == CALL_INSN)
past_call = 1;
if (GET_CODE (temp) == NOTE)
{
temp = PREV_INSN (temp);
continue;
}
set = single_set (temp);
if (!set)
{
temp = PREV_INSN (temp);
continue;
}
if (REG_P (SET_DEST (set))
&& SET_SRC (set) == CONST0_RTX (GET_MODE (SET_DEST (set)))
&& !reg_set_between_p (SET_DEST (set), temp, insn)
&& (REGNO_REG_CLASS (REGNO (SET_DEST (set)))
== REGNO_REG_CLASS (REGNO (operand)))
&& REGNO_REG_CLASS (REGNO (SET_DEST (set))) != EXTENDED_REGS
&& REGNO (SET_DEST (set)) != REGNO (operand)
&& (!past_call
|| !call_used_regs[REGNO (SET_DEST (set))]))
{
rtx xoperands[2];
xoperands[0] = operand;
xoperands[1] = SET_DEST (set);
output_asm_insn ("cmp %1,%0", xoperands);
return "";
}
if (REGNO_REG_CLASS (REGNO (operand)) == EXTENDED_REGS
&& REG_P (SET_DEST (set))
&& SET_SRC (set) == CONST0_RTX (GET_MODE (SET_DEST (set)))
&& !reg_set_between_p (SET_DEST (set), temp, insn)
&& (REGNO_REG_CLASS (REGNO (SET_DEST (set)))
!= REGNO_REG_CLASS (REGNO (operand)))
&& REGNO_REG_CLASS (REGNO (SET_DEST (set))) == EXTENDED_REGS
&& REGNO (SET_DEST (set)) != REGNO (operand)
&& (!past_call
|| !call_used_regs[REGNO (SET_DEST (set))]))
{
rtx xoperands[2];
xoperands[0] = operand;
xoperands[1] = SET_DEST (set);
output_asm_insn ("cmp %1,%0", xoperands);
return "";
}
temp = PREV_INSN (temp);
}
return "cmp 0,%0";
}
int
impossible_plus_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
{
if (GET_CODE (op) != PLUS)
return 0;
if (XEXP (op, 0) == stack_pointer_rtx
|| XEXP (op, 1) == stack_pointer_rtx)
return 1;
return 0;
}
int
const_8bit_operand (register rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
{
return (GET_CODE (op) == CONST_INT
&& INTVAL (op) >= 0
&& INTVAL (op) < 256);
}
int
const_1f_operand (register rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
{
return (op == CONST1_RTX (SFmode));
}
int
mask_ok_for_mem_btst (int len, int bit)
{
unsigned int mask = 0;
while (len > 0)
{
mask |= (1 << bit);
bit++;
len--;
}
return (((mask & 0xff) == mask)
|| ((mask & 0xff00) == mask)
|| ((mask & 0xff0000) == mask)
|| ((mask & 0xff000000) == mask));
}
int
symbolic_operand (register rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
{
switch (GET_CODE (op))
{
case SYMBOL_REF:
case LABEL_REF:
return 1;
case CONST:
op = XEXP (op, 0);
return ((GET_CODE (XEXP (op, 0)) == SYMBOL_REF
|| GET_CODE (XEXP (op, 0)) == LABEL_REF)
&& GET_CODE (XEXP (op, 1)) == CONST_INT);
default:
return 0;
}
}
rtx
legitimize_address (rtx x, rtx oldx ATTRIBUTE_UNUSED,
enum machine_mode mode ATTRIBUTE_UNUSED)
{
if (flag_pic && ! legitimate_pic_operand_p (x))
x = legitimize_pic_address (oldx, NULL_RTX);
if (GET_CODE (x) == PLUS
&& symbolic_operand (XEXP (x, 1), VOIDmode))
{
rtx regx1, regy1, regy2, y;
y = XEXP (x, 1);
if (GET_CODE (y) == CONST)
y = XEXP (y, 0);
if (GET_CODE (y) == PLUS || GET_CODE (y) == MINUS)
{
regx1 = force_reg (Pmode, force_operand (XEXP (x, 0), 0));
regy1 = force_reg (Pmode, force_operand (XEXP (y, 0), 0));
regy2 = force_reg (Pmode, force_operand (XEXP (y, 1), 0));
regx1 = force_reg (Pmode,
gen_rtx_fmt_ee (GET_CODE (y), Pmode, regx1, regy2));
return force_reg (Pmode, gen_rtx_PLUS (Pmode, regx1, regy1));
}
}
return x;
}
rtx
legitimize_pic_address (rtx orig, rtx reg)
{
if (GET_CODE (orig) == LABEL_REF
|| (GET_CODE (orig) == SYMBOL_REF
&& (CONSTANT_POOL_ADDRESS_P (orig)
|| ! MN10300_GLOBAL_P (orig))))
{
if (reg == 0)
reg = gen_reg_rtx (Pmode);
emit_insn (gen_symGOTOFF2reg (reg, orig));
return reg;
}
else if (GET_CODE (orig) == SYMBOL_REF)
{
if (reg == 0)
reg = gen_reg_rtx (Pmode);
emit_insn (gen_symGOT2reg (reg, orig));
return reg;
}
return orig;
}
int
legitimate_pic_operand_p (rtx x)
{
register const char *fmt;
register int i;
if (GET_CODE (x) == SYMBOL_REF || GET_CODE (x) == LABEL_REF)
return 0;
if (GET_CODE (x) == UNSPEC
&& (XINT (x, 1) == UNSPEC_PIC
|| XINT (x, 1) == UNSPEC_GOT
|| XINT (x, 1) == UNSPEC_GOTOFF
|| XINT (x, 1) == UNSPEC_PLT))
return 1;
fmt = GET_RTX_FORMAT (GET_CODE (x));
for (i = GET_RTX_LENGTH (GET_CODE (x)) - 1; i >= 0; i--)
{
if (fmt[i] == 'E')
{
register int j;
for (j = XVECLEN (x, i) - 1; j >= 0; j--)
if (! legitimate_pic_operand_p (XVECEXP (x, i, j)))
return 0;
}
else if (fmt[i] == 'e' && ! legitimate_pic_operand_p (XEXP (x, i)))
return 0;
}
return 1;
}
bool
legitimate_address_p (enum machine_mode mode, rtx x, int strict)
{
if (CONSTANT_ADDRESS_P (x)
&& (! flag_pic || legitimate_pic_operand_p (x)))
return TRUE;
if (RTX_OK_FOR_BASE_P (x, strict))
return TRUE;
if (TARGET_AM33
&& GET_CODE (x) == POST_INC
&& RTX_OK_FOR_BASE_P (XEXP (x, 0), strict)
&& (mode == SImode || mode == SFmode || mode == HImode))
return TRUE;
if (GET_CODE (x) == PLUS)
{
rtx base = 0, index = 0;
if (REG_P (XEXP (x, 0))
&& REGNO_STRICT_OK_FOR_BASE_P (REGNO (XEXP (x, 0)), strict))
{
base = XEXP (x, 0);
index = XEXP (x, 1);
}
if (REG_P (XEXP (x, 1))
&& REGNO_STRICT_OK_FOR_BASE_P (REGNO (XEXP (x, 1)), strict))
{
base = XEXP (x, 1);
index = XEXP (x, 0);
}
if (base != 0 && index != 0)
{
if (GET_CODE (index) == CONST_INT)
return TRUE;
if (GET_CODE (index) == CONST
&& GET_CODE (XEXP (index, 0)) != PLUS
&& (! flag_pic
|| legitimate_pic_operand_p (index)))
return TRUE;
}
}
return FALSE;
}
static int
mn10300_address_cost_1 (rtx x, int *unsig)
{
switch (GET_CODE (x))
{
case REG:
switch (REGNO_REG_CLASS (REGNO (x)))
{
case SP_REGS:
*unsig = 1;
return 0;
case ADDRESS_REGS:
return 1;
case DATA_REGS:
case EXTENDED_REGS:
case FP_REGS:
return 3;
case NO_REGS:
return 5;
default:
abort ();
}
case PLUS:
case MINUS:
case ASHIFT:
case AND:
case IOR:
return (mn10300_address_cost_1 (XEXP (x, 0), unsig)
+ mn10300_address_cost_1 (XEXP (x, 1), unsig));
case EXPR_LIST:
case SUBREG:
case MEM:
return mn10300_address_cost (XEXP (x, 0));
case ZERO_EXTEND:
*unsig = 1;
return mn10300_address_cost_1 (XEXP (x, 0), unsig);
case CONST_INT:
if (INTVAL (x) == 0)
return 0;
if (INTVAL (x) + (*unsig ? 0 : 0x80) < 0x100)
return 1;
if (INTVAL (x) + (*unsig ? 0 : 0x8000) < 0x10000)
return 3;
if (INTVAL (x) + (*unsig ? 0 : 0x800000) < 0x1000000)
return 5;
return 7;
case CONST:
case SYMBOL_REF:
case LABEL_REF:
return 8;
default:
abort ();
}
}
static int
mn10300_address_cost (rtx x)
{
int s = 0;
return mn10300_address_cost_1 (x, &s);
}
static bool
mn10300_rtx_costs (rtx x, int code, int outer_code, int *total)
{
switch (code)
{
case CONST_INT:
if (INTVAL (x) == 0 && outer_code == SET)
*total = 0;
else if (INT_8_BITS (INTVAL (x)))
*total = 1;
else if (INT_16_BITS (INTVAL (x))
|| (INTVAL (x) & 0xffff) == 0
|| (INTVAL (x) & 0xffff0000) == 0)
*total = 2;
else
*total = 4;
return true;
case CONST:
case LABEL_REF:
case SYMBOL_REF:
*total = 6;
return true;
case CONST_DOUBLE:
*total = 8;
return true;
case MOD:
case DIV:
case MULT:
*total = 8;
return true;
default:
return false;
}
}
bool
mn10300_wide_const_load_uses_clr (rtx operands[2])
{
long val[2];
if (GET_CODE (operands[0]) != REG
|| REGNO_REG_CLASS (REGNO (operands[0])) != DATA_REGS)
return false;
switch (GET_CODE (operands[1]))
{
case CONST_INT:
{
rtx low, high;
split_double (operands[1], &low, &high);
val[0] = INTVAL (low);
val[1] = INTVAL (high);
}
break;
case CONST_DOUBLE:
if (GET_MODE (operands[1]) == DFmode)
{
REAL_VALUE_TYPE rv;
REAL_VALUE_FROM_CONST_DOUBLE (rv, operands[1]);
REAL_VALUE_TO_TARGET_DOUBLE (rv, val);
}
else if (GET_MODE (operands[1]) == VOIDmode
|| GET_MODE (operands[1]) == DImode)
{
val[0] = CONST_DOUBLE_LOW (operands[1]);
val[1] = CONST_DOUBLE_HIGH (operands[1]);
}
break;
default:
return false;
}
return val[0] == 0 || val[1] == 0;
}
static void
mn10300_encode_section_info (tree decl, rtx rtl, int first ATTRIBUTE_UNUSED)
{
rtx symbol;
if (GET_CODE (rtl) != MEM)
return;
symbol = XEXP (rtl, 0);
if (GET_CODE (symbol) != SYMBOL_REF)
return;
if (flag_pic)
SYMBOL_REF_FLAG (symbol) = (*targetm.binds_local_p) (decl);
}