#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 "recog.h"
#include "toplev.h"
#include "output.h"
#include "integrate.h"
#include "tree.h"
#include "function.h"
#include "expr.h"
#include "optabs.h"
#include "libfuncs.h"
#include "flags.h"
#include "tm_p.h"
#include "ggc.h"
#include "insn-flags.h"
#include "obstack.h"
#include "except.h"
#include "target.h"
#include "target-def.h"
#include "basic-block.h"
#define FP_MASK (1 << (GPR_FP))
#define LINK_MASK (1 << (GPR_LINK))
#define ROUND_ADVANCE(SIZE) (((SIZE) + UNITS_PER_WORD - 1) / UNITS_PER_WORD)
struct machine_function GTY(())
{
int ra_needs_full_frame;
struct rtx_def * eh_stack_adjust;
int interrupt_handler;
int has_loops;
};
struct rtx_def * mt_compare_op0;
struct rtx_def * mt_compare_op1;
struct mt_frame_info current_frame_info;
struct mt_frame_info zero_frame_info;
struct rtx_def * mt_ucmpsi3_libcall;
static int mt_flag_delayed_branch;
static rtx
mt_struct_value_rtx (tree fndecl ATTRIBUTE_UNUSED,
int incoming ATTRIBUTE_UNUSED)
{
return gen_rtx_REG (Pmode, RETVAL_REGNUM);
}
rtx
mt_return_addr_rtx (int count)
{
if (count != 0)
return NULL_RTX;
return get_hard_reg_initial_val (Pmode, GPR_LINK);
}
static int mt_nops_required = 0;
static const char * mt_nop_reasons = "";
const char *
mt_asm_output_opcode (FILE *f ATTRIBUTE_UNUSED, const char *ptr)
{
if (mt_nops_required)
fprintf (f, ";# need %d nops because of %s\n\t",
mt_nops_required, mt_nop_reasons);
while (mt_nops_required)
{
fprintf (f, "nop\n\t");
-- mt_nops_required;
}
return ptr;
}
static enum attr_type
mt_get_attr_type (rtx complete_insn)
{
rtx insn = PATTERN (complete_insn);
if (JUMP_P (complete_insn))
return TYPE_BRANCH;
if (CALL_P (complete_insn))
return TYPE_BRANCH;
if (GET_CODE (insn) != SET)
return TYPE_ARITH;
if (SET_DEST (insn) == pc_rtx)
return TYPE_BRANCH;
if (GET_CODE (SET_DEST (insn)) == MEM)
return TYPE_STORE;
if (GET_CODE (SET_SRC (insn)) == MEM)
return TYPE_LOAD;
return TYPE_ARITH;
}
static void
insn_dependent_p_1 (rtx x, rtx pat ATTRIBUTE_UNUSED, void *data)
{
rtx * pinsn = (rtx *) data;
if (*pinsn && reg_mentioned_p (x, *pinsn))
*pinsn = NULL_RTX;
}
static bool
insn_dependent_p (rtx x, rtx y)
{
rtx tmp;
if (! INSN_P (x) || ! INSN_P (y))
return 0;
tmp = PATTERN (y);
note_stores (PATTERN (x), insn_dependent_p_1, &tmp);
if (tmp == NULL_RTX)
return true;
tmp = PATTERN (x);
note_stores (PATTERN (y), insn_dependent_p_1, &tmp);
return (tmp == NULL_RTX);
}
static bool
insn_true_dependent_p (rtx x, rtx y)
{
rtx tmp;
if (! INSN_P (x) || ! INSN_P (y))
return 0;
tmp = PATTERN (y);
note_stores (PATTERN (x), insn_dependent_p_1, &tmp);
return (tmp == NULL_RTX);
}
void
mt_final_prescan_insn (rtx insn,
rtx * opvec ATTRIBUTE_UNUSED,
int noperands ATTRIBUTE_UNUSED)
{
rtx prev_i;
enum attr_type prev_attr;
mt_nops_required = 0;
mt_nop_reasons = "";
if (TARGET_MS2)
return;
if (! INSN_P (insn))
return;
for (prev_i = PREV_INSN (insn);
prev_i != NULL
&& (! INSN_P (prev_i)
|| GET_CODE (PATTERN (prev_i)) == USE
|| GET_CODE (PATTERN (prev_i)) == CLOBBER);
prev_i = PREV_INSN (prev_i))
{
if (BARRIER_P (prev_i))
return;
}
if (prev_i == NULL || ! INSN_P (prev_i))
return;
prev_attr = mt_get_attr_type (prev_i);
if (prev_attr == TYPE_BRANCH)
return;
switch (mt_get_attr_type (insn))
{
case TYPE_LOAD:
case TYPE_STORE:
if ((prev_attr == TYPE_LOAD || prev_attr == TYPE_STORE)
&& TARGET_MS1_64_001)
{
mt_nops_required = 1;
mt_nop_reasons = "consecutive mem ops";
}
case TYPE_ARITH:
case TYPE_COMPLEX:
if (prev_attr == TYPE_LOAD
&& insn_true_dependent_p (prev_i, insn))
{
mt_nops_required = 1;
mt_nop_reasons = "load->arith dependency delay";
}
break;
case TYPE_BRANCH:
if (insn_dependent_p (prev_i, insn))
{
if (prev_attr == TYPE_ARITH && TARGET_MS1_64_001)
{
mt_nops_required = 1;
mt_nop_reasons = "arith->branch dependency delay";
}
else if (prev_attr == TYPE_LOAD)
{
if (TARGET_MS1_64_001)
mt_nops_required = 2;
else
mt_nops_required = 1;
mt_nop_reasons = "load->branch dependency delay";
}
}
break;
default:
fatal_insn ("mt_final_prescan_insn, invalid insn #1", insn);
break;
}
}
static void
mt_debug_stack (struct mt_frame_info * info)
{
int regno;
if (!info)
{
error ("info pointer NULL");
gcc_unreachable ();
}
fprintf (stderr, "\nStack information for function %s:\n",
((current_function_decl && DECL_NAME (current_function_decl))
? IDENTIFIER_POINTER (DECL_NAME (current_function_decl))
: "<unknown>"));
fprintf (stderr, "\ttotal_size = %d\n", info->total_size);
fprintf (stderr, "\tpretend_size = %d\n", info->pretend_size);
fprintf (stderr, "\targs_size = %d\n", info->args_size);
fprintf (stderr, "\textra_size = %d\n", info->extra_size);
fprintf (stderr, "\treg_size = %d\n", info->reg_size);
fprintf (stderr, "\tvar_size = %d\n", info->var_size);
fprintf (stderr, "\tframe_size = %d\n", info->frame_size);
fprintf (stderr, "\treg_mask = 0x%x\n", info->reg_mask);
fprintf (stderr, "\tsave_fp = %d\n", info->save_fp);
fprintf (stderr, "\tsave_lr = %d\n", info->save_lr);
fprintf (stderr, "\tinitialized = %d\n", info->initialized);
fprintf (stderr, "\tsaved registers =");
for (regno = GPR_R0; regno <= GPR_LAST; regno++)
if ( (1 << regno) & info->reg_mask)
fprintf (stderr, " %s", reg_names[regno]);
putc ('\n', stderr);
fflush (stderr);
}
static void
mt_print_operand_simple_address (FILE * file, rtx addr)
{
if (!addr)
error ("PRINT_OPERAND_ADDRESS, null pointer");
else
switch (GET_CODE (addr))
{
case REG:
fprintf (file, "%s, #0", reg_names [REGNO (addr)]);
break;
case PLUS:
{
rtx reg = 0;
rtx offset = 0;
rtx arg0 = XEXP (addr, 0);
rtx arg1 = XEXP (addr, 1);
if (GET_CODE (arg0) == REG)
{
reg = arg0;
offset = arg1;
if (GET_CODE (offset) == REG)
fatal_insn ("PRINT_OPERAND_ADDRESS, 2 regs", addr);
}
else if (GET_CODE (arg1) == REG)
reg = arg1, offset = arg0;
else if (CONSTANT_P (arg0) && CONSTANT_P (arg1))
{
fprintf (file, "%s, #", reg_names [GPR_R0]);
output_addr_const (file, addr);
break;
}
fprintf (file, "%s, #", reg_names [REGNO (reg)]);
output_addr_const (file, offset);
break;
}
case LABEL_REF:
case SYMBOL_REF:
case CONST_INT:
case CONST:
output_addr_const (file, addr);
break;
default:
fatal_insn ("PRINT_OPERAND_ADDRESS, invalid insn #1", addr);
break;
}
}
void
mt_print_operand_address (FILE * file, rtx addr)
{
if (GET_CODE (addr) == AND
&& GET_CODE (XEXP (addr, 1)) == CONST_INT
&& INTVAL (XEXP (addr, 1)) == -3)
mt_print_operand_simple_address (file, XEXP (addr, 0));
else
mt_print_operand_simple_address (file, addr);
}
void
mt_print_operand (FILE * file, rtx x, int code)
{
switch (code)
{
case '#':
if (dbr_sequence_length () == 0)
fputs ("\n\tnop", file);
return;
case 'H':
fprintf(file, "#%%hi16(");
output_addr_const (file, x);
fprintf(file, ")");
return;
case 'L':
fprintf(file, "#%%lo16(");
output_addr_const (file, x);
fprintf(file, ")");
return;
case 'N':
fprintf(file, "#%ld", ~INTVAL (x));
return;
case 'z':
if (GET_CODE (x) == CONST_INT && INTVAL (x) == 0)
{
fputs (reg_names[GPR_R0], file);
return;
}
case 0:
break;
default:
fprintf (file, "unknown code");
return;
}
switch (GET_CODE (x))
{
case REG:
fputs (reg_names [REGNO (x)], file);
break;
case CONST:
case CONST_INT:
fprintf(file, "#%ld", INTVAL (x));
break;
case MEM:
mt_print_operand_address(file, XEXP (x,0));
break;
case LABEL_REF:
case SYMBOL_REF:
output_addr_const (file, x);
break;
default:
fprintf(file, "Uknown code: %d", GET_CODE (x));
break;
}
return;
}
void
mt_init_cumulative_args (CUMULATIVE_ARGS * cum, tree fntype, rtx libname,
tree fndecl ATTRIBUTE_UNUSED, int incoming)
{
*cum = 0;
if (TARGET_DEBUG_ARG)
{
fprintf (stderr, "\nmt_init_cumulative_args:");
if (incoming)
fputs (" incoming", stderr);
if (fntype)
{
tree ret_type = TREE_TYPE (fntype);
fprintf (stderr, " return = %s,",
tree_code_name[ (int)TREE_CODE (ret_type) ]);
}
if (libname && GET_CODE (libname) == SYMBOL_REF)
fprintf (stderr, " libname = %s", XSTR (libname, 0));
if (cfun->returns_struct)
fprintf (stderr, " return-struct");
putc ('\n', stderr);
}
}
static int
mt_function_arg_slotno (const CUMULATIVE_ARGS * cum,
enum machine_mode mode,
tree type,
int named ATTRIBUTE_UNUSED,
int incoming_p ATTRIBUTE_UNUSED,
int * pregno)
{
int regbase = FIRST_ARG_REGNUM;
int slotno = * cum;
if (mode == VOIDmode || targetm.calls.must_pass_in_stack (mode, type))
return -1;
if (slotno >= MT_NUM_ARG_REGS)
return -1;
* pregno = regbase + slotno;
return slotno;
}
rtx
mt_function_arg (const CUMULATIVE_ARGS * cum,
enum machine_mode mode,
tree type,
int named,
int incoming_p)
{
int slotno, regno;
rtx reg;
slotno = mt_function_arg_slotno (cum, mode, type, named, incoming_p, ®no);
if (slotno == -1)
reg = NULL_RTX;
else
reg = gen_rtx_REG (mode, regno);
return reg;
}
void
mt_function_arg_advance (CUMULATIVE_ARGS * cum,
enum machine_mode mode,
tree type ATTRIBUTE_UNUSED,
int named)
{
int slotno, regno;
slotno = mt_function_arg_slotno (cum, mode, type, named, 0, ®no);
* cum += (mode != BLKmode
? ROUND_ADVANCE (GET_MODE_SIZE (mode))
: ROUND_ADVANCE (int_size_in_bytes (type)));
if (TARGET_DEBUG_ARG)
fprintf (stderr,
"mt_function_arg_advance: words = %2d, mode = %4s, named = %d, size = %3d\n",
*cum, GET_MODE_NAME (mode), named,
(*cum) * UNITS_PER_WORD);
}
static int
mt_arg_partial_bytes (CUMULATIVE_ARGS * pcum,
enum machine_mode mode,
tree type,
bool named ATTRIBUTE_UNUSED)
{
int cum = * pcum;
int words;
if (mode == BLKmode)
words = ((int_size_in_bytes (type) + UNITS_PER_WORD - 1)
/ UNITS_PER_WORD);
else
words = (GET_MODE_SIZE (mode) + UNITS_PER_WORD - 1) / UNITS_PER_WORD;
if (! targetm.calls.pass_by_reference (&cum, mode, type, named)
&& cum < MT_NUM_ARG_REGS
&& (cum + words) > MT_NUM_ARG_REGS)
{
int bytes = (MT_NUM_ARG_REGS - cum) * UNITS_PER_WORD;
if (TARGET_DEBUG)
fprintf (stderr, "function_arg_partial_nregs = %d\n", bytes);
return bytes;
}
return 0;
}
static bool
mt_pass_by_reference (CUMULATIVE_ARGS * cum ATTRIBUTE_UNUSED,
enum machine_mode mode ATTRIBUTE_UNUSED,
tree type,
bool named ATTRIBUTE_UNUSED)
{
return (type && int_size_in_bytes (type) > 4 * UNITS_PER_WORD);
}
int
mt_function_arg_boundary (enum machine_mode mode ATTRIBUTE_UNUSED,
tree type ATTRIBUTE_UNUSED)
{
return BITS_PER_WORD;
}
int
mt_reg_ok_for_base_p (rtx x, int strict)
{
if (strict)
return (((unsigned) REGNO (x)) < FIRST_PSEUDO_REGISTER);
return 1;
}
static bool
mt_legitimate_simple_address_p (enum machine_mode mode ATTRIBUTE_UNUSED,
rtx xinsn, int strict)
{
if (TARGET_DEBUG)
{
fprintf (stderr, "\n========== GO_IF_LEGITIMATE_ADDRESS, %sstrict\n",
strict ? "" : "not ");
debug_rtx (xinsn);
}
if (GET_CODE (xinsn) == REG && mt_reg_ok_for_base_p (xinsn, strict))
return true;
if (GET_CODE (xinsn) == PLUS
&& GET_CODE (XEXP (xinsn, 0)) == REG
&& mt_reg_ok_for_base_p (XEXP (xinsn, 0), strict)
&& GET_CODE (XEXP (xinsn, 1)) == CONST_INT
&& SMALL_INT (XEXP (xinsn, 1)))
return true;
return false;
}
int
mt_legitimate_address_p (enum machine_mode mode, rtx xinsn, int strict)
{
if (mt_legitimate_simple_address_p (mode, xinsn, strict))
return 1;
if ((mode) == SImode
&& GET_CODE (xinsn) == AND
&& GET_CODE (XEXP (xinsn, 1)) == CONST_INT
&& INTVAL (XEXP (xinsn, 1)) == -3)
return mt_legitimate_simple_address_p (mode, XEXP (xinsn, 0), strict);
else
return 0;
}
int
uns_arith_operand (rtx op, enum machine_mode mode)
{
if (GET_CODE (op) == CONST_INT && SMALL_INT_UNSIGNED (op))
return 1;
return register_operand (op, mode);
}
int
arith_operand (rtx op, enum machine_mode mode)
{
if (GET_CODE (op) == CONST_INT && SMALL_INT (op))
return 1;
return register_operand (op, mode);
}
int
reg_or_0_operand (rtx op, enum machine_mode mode)
{
switch (GET_CODE (op))
{
case CONST_INT:
return INTVAL (op) == 0;
case REG:
case SUBREG:
return register_operand (op, mode);
default:
break;
}
return 0;
}
int
big_const_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
{
if (GET_CODE (op) == CONST_INT && CONST_OK_FOR_LETTER_P (INTVAL (op), 'M'))
return 1;
return 0;
}
int
single_const_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
{
if (big_const_operand (op, mode)
|| GET_CODE (op) == CONST
|| GET_CODE (op) == LABEL_REF
|| GET_CODE (op) == SYMBOL_REF)
return 0;
return 1;
}
int interrupt_handler;
enum processor_type mt_cpu;
static struct machine_function *
mt_init_machine_status (void)
{
struct machine_function *f;
f = ggc_alloc_cleared (sizeof (struct machine_function));
return f;
}
void
mt_override_options (void)
{
if (mt_cpu_string != NULL)
{
if (!strcmp (mt_cpu_string, "ms1-64-001"))
mt_cpu = PROCESSOR_MS1_64_001;
else if (!strcmp (mt_cpu_string, "ms1-16-002"))
mt_cpu = PROCESSOR_MS1_16_002;
else if (!strcmp (mt_cpu_string, "ms1-16-003"))
mt_cpu = PROCESSOR_MS1_16_003;
else if (!strcmp (mt_cpu_string, "ms2"))
mt_cpu = PROCESSOR_MS2;
else
error ("bad value (%s) for -march= switch", mt_cpu_string);
}
else
mt_cpu = PROCESSOR_MS1_16_002;
if (flag_exceptions)
{
flag_omit_frame_pointer = 0;
flag_gcse = 0;
}
mt_flag_delayed_branch = flag_delayed_branch;
flag_delayed_branch = 0;
init_machine_status = mt_init_machine_status;
}
static void
mt_setup_incoming_varargs (CUMULATIVE_ARGS *cum,
enum machine_mode mode ATTRIBUTE_UNUSED,
tree type ATTRIBUTE_UNUSED,
int *pretend_size, int no_rtl)
{
int regno;
int regs = MT_NUM_ARG_REGS - *cum;
*pretend_size = regs < 0 ? 0 : GET_MODE_SIZE (SImode) * regs;
if (no_rtl)
return;
for (regno = *cum; regno < MT_NUM_ARG_REGS; regno++)
{
rtx reg = gen_rtx_REG (SImode, FIRST_ARG_REGNUM + regno);
rtx slot = gen_rtx_PLUS (Pmode,
gen_rtx_REG (SImode, ARG_POINTER_REGNUM),
GEN_INT (UNITS_PER_WORD * regno));
emit_move_insn (gen_rtx_MEM (SImode, slot), reg);
}
}
unsigned int
mt_compute_frame_size (int size)
{
int regno;
unsigned int total_size;
unsigned int var_size;
unsigned int args_size;
unsigned int pretend_size;
unsigned int extra_size;
unsigned int reg_size;
unsigned int frame_size;
unsigned int reg_mask;
var_size = size;
args_size = current_function_outgoing_args_size;
pretend_size = current_function_pretend_args_size;
extra_size = FIRST_PARM_OFFSET (0);
total_size = extra_size + pretend_size + args_size + var_size;
reg_size = 0;
reg_mask = 0;
for (regno = GPR_R0; regno <= GPR_LAST; regno++)
{
if (MUST_SAVE_REGISTER (regno))
{
reg_size += UNITS_PER_WORD;
reg_mask |= 1 << regno;
}
}
current_frame_info.save_fp = (regs_ever_live [GPR_FP]
|| frame_pointer_needed
|| interrupt_handler);
current_frame_info.save_lr = (regs_ever_live [GPR_LINK]
|| profile_flag
|| interrupt_handler);
reg_size += (current_frame_info.save_fp + current_frame_info.save_lr)
* UNITS_PER_WORD;
total_size += reg_size;
total_size = ((total_size + 3) & ~3);
frame_size = total_size;
current_frame_info.pretend_size = pretend_size;
current_frame_info.var_size = var_size;
current_frame_info.args_size = args_size;
current_frame_info.reg_size = reg_size;
current_frame_info.frame_size = args_size + var_size;
current_frame_info.total_size = total_size;
current_frame_info.extra_size = extra_size;
current_frame_info.reg_mask = reg_mask;
current_frame_info.initialized = reload_completed;
return total_size;
}
static void
mt_emit_save_restore (enum save_direction direction,
rtx reg, rtx mem, int stack_offset)
{
if (direction == FROM_PROCESSOR_TO_MEM)
{
rtx insn;
insn = emit_move_insn (mem, reg);
RTX_FRAME_RELATED_P (insn) = 1;
REG_NOTES (insn)
= gen_rtx_EXPR_LIST
(REG_FRAME_RELATED_EXPR,
gen_rtx_SET (VOIDmode,
gen_rtx_MEM (SImode,
gen_rtx_PLUS (SImode,
stack_pointer_rtx,
GEN_INT (stack_offset))),
reg),
REG_NOTES (insn));
}
else
emit_move_insn (reg, mem);
}
static void
mt_emit_save_fp (enum save_direction direction,
struct mt_frame_info info)
{
rtx base_reg;
int reg_mask = info.reg_mask & ~(FP_MASK | LINK_MASK);
int offset = info.total_size;
int stack_offset = info.total_size;
if (! info.save_fp && ! info.save_lr && ! reg_mask)
return;
if (CONST_OK_FOR_LETTER_P(offset, 'O'))
base_reg = stack_pointer_rtx;
else
{
base_reg = gen_rtx_REG (SImode, GPR_R9);
offset = 0;
}
if (info.save_fp)
{
offset -= UNITS_PER_WORD;
stack_offset -= UNITS_PER_WORD;
mt_emit_save_restore
(direction, gen_rtx_REG (SImode, GPR_FP),
gen_rtx_MEM (SImode,
gen_rtx_PLUS (SImode, base_reg, GEN_INT (offset))),
stack_offset);
}
}
static void
mt_emit_save_regs (enum save_direction direction,
struct mt_frame_info info)
{
rtx base_reg;
int regno;
int reg_mask = info.reg_mask & ~(FP_MASK | LINK_MASK);
int offset = info.total_size;
int stack_offset = info.total_size;
if (! info.save_fp && ! info.save_lr && ! reg_mask)
return;
if (CONST_OK_FOR_LETTER_P(offset, 'O'))
base_reg = stack_pointer_rtx;
else
{
base_reg = gen_rtx_REG (SImode, GPR_R9);
offset = 0;
}
if (info.save_fp)
{
offset -= UNITS_PER_WORD;
stack_offset -= UNITS_PER_WORD;
}
if (info.save_lr)
{
offset -= UNITS_PER_WORD;
stack_offset -= UNITS_PER_WORD;
mt_emit_save_restore
(direction, gen_rtx_REG (SImode, GPR_LINK),
gen_rtx_MEM (SImode,
gen_rtx_PLUS (SImode, base_reg, GEN_INT (offset))),
stack_offset);
}
for (regno = GPR_R0; regno <= GPR_LAST; regno++)
{
if ((reg_mask & (1 << regno)) != 0)
{
offset -= UNITS_PER_WORD;
stack_offset -= UNITS_PER_WORD;
mt_emit_save_restore
(direction, gen_rtx_REG (SImode, regno),
gen_rtx_MEM (SImode,
gen_rtx_PLUS (SImode, base_reg, GEN_INT (offset))),
stack_offset);
}
}
}
static bool
mt_interrupt_function_p (tree func)
{
tree a;
if (TREE_CODE (func) != FUNCTION_DECL)
return false;
a = lookup_attribute ("interrupt", DECL_ATTRIBUTES (func));
return a != NULL_TREE;
}
void
mt_expand_prologue (void)
{
rtx size_rtx, insn;
unsigned int frame_size;
if (mt_interrupt_function_p (current_function_decl))
{
interrupt_handler = 1;
if (cfun->machine)
cfun->machine->interrupt_handler = 1;
}
mt_compute_frame_size (get_frame_size ());
if (TARGET_DEBUG_STACK)
mt_debug_stack (¤t_frame_info);
frame_size = current_frame_info.total_size;
if (CONST_OK_FOR_LETTER_P(frame_size, 'O'))
size_rtx = GEN_INT (frame_size);
else
{
gcc_assert (!interrupt_handler);
size_rtx = gen_rtx_REG (SImode, GPR_R9);
insn = emit_move_insn (size_rtx, GEN_INT (frame_size & 0xffff0000));
insn = emit_insn (gen_iorsi3 (size_rtx, size_rtx,
GEN_INT (frame_size & 0x0000ffff)));
}
if (frame_size)
{
insn = emit_insn (gen_subsi3 (stack_pointer_rtx,
stack_pointer_rtx,
size_rtx));
RTX_FRAME_RELATED_P (insn) = 1;
REG_NOTES (insn)
= gen_rtx_EXPR_LIST (REG_FRAME_RELATED_EXPR,
gen_rtx_SET (VOIDmode,
stack_pointer_rtx,
gen_rtx_MINUS (SImode,
stack_pointer_rtx,
GEN_INT (frame_size))),
REG_NOTES (insn));
}
if ( current_frame_info.reg_size != 0
&& !CONST_OK_FOR_LETTER_P (frame_size, 'O'))
emit_insn (gen_addsi3 (size_rtx, size_rtx, stack_pointer_rtx));
mt_emit_save_fp (FROM_PROCESSOR_TO_MEM, current_frame_info);
if (frame_pointer_needed)
{
insn = emit_move_insn (frame_pointer_rtx, stack_pointer_rtx);
RTX_FRAME_RELATED_P (insn) = 1;
}
mt_emit_save_regs (FROM_PROCESSOR_TO_MEM, current_frame_info);
if (profile_flag)
emit_insn (gen_blockage ());
}
int
mt_epilogue_uses (int regno)
{
if (cfun->machine && cfun->machine->interrupt_handler && reload_completed)
return 1;
return regno == GPR_LINK;
}
void
mt_expand_epilogue (enum epilogue_type eh_mode)
{
rtx size_rtx, insn;
unsigned frame_size;
mt_compute_frame_size (get_frame_size ());
if (TARGET_DEBUG_STACK)
mt_debug_stack (& current_frame_info);
frame_size = current_frame_info.total_size;
if (CONST_OK_FOR_LETTER_P(frame_size, 'O'))
size_rtx = GEN_INT (frame_size);
else
{
gcc_assert (!interrupt_handler);
size_rtx = gen_rtx_REG (SImode, GPR_R9);
insn = emit_move_insn (size_rtx, GEN_INT (frame_size & 0xffff0000));
insn = emit_insn (gen_iorsi3 (size_rtx, size_rtx,
GEN_INT (frame_size & 0x0000ffff)));
emit_insn (gen_addsi3 (size_rtx, size_rtx, stack_pointer_rtx));
}
if (frame_pointer_needed)
insn = emit_move_insn (stack_pointer_rtx, frame_pointer_rtx);
mt_emit_save_fp (FROM_MEM_TO_PROCESSOR, current_frame_info);
mt_emit_save_regs (FROM_MEM_TO_PROCESSOR, current_frame_info);
if (frame_size)
{
if (CONST_OK_FOR_LETTER_P(frame_size, 'O'))
insn = emit_insn (gen_addsi3 (stack_pointer_rtx,
stack_pointer_rtx,
size_rtx));
else
insn = emit_move_insn (stack_pointer_rtx,
gen_rtx_REG (SImode, GPR_R9));
REG_NOTES (insn)
= gen_rtx_EXPR_LIST (REG_FRAME_RELATED_EXPR,
gen_rtx_SET (VOIDmode,
stack_pointer_rtx,
gen_rtx_PLUS (SImode,
stack_pointer_rtx,
GEN_INT (frame_size))),
REG_NOTES (insn));
}
if (cfun->machine && cfun->machine->eh_stack_adjust != NULL_RTX)
emit_insn (gen_addsi3 (stack_pointer_rtx,
stack_pointer_rtx,
cfun->machine->eh_stack_adjust));
if (eh_mode == EH_EPILOGUE)
{
emit_jump_insn (gen_eh_return_internal ());
emit_barrier ();
}
else if (interrupt_handler)
emit_jump_insn (gen_return_interrupt_internal ());
else
emit_jump_insn (gen_return_internal ());
interrupt_handler = 0;
current_frame_info = zero_frame_info;
if (cfun->machine)
cfun->machine->eh_stack_adjust = NULL_RTX;
}
void
mt_expand_eh_return (rtx * operands)
{
if (GET_CODE (operands[0]) != REG
|| REGNO (operands[0]) != EH_RETURN_STACKADJ_REGNO)
{
rtx sp = EH_RETURN_STACKADJ_RTX;
emit_move_insn (sp, operands[0]);
operands[0] = sp;
}
emit_insn (gen_eh_epilogue (operands[0]));
}
void
mt_emit_eh_epilogue (rtx * operands ATTRIBUTE_UNUSED)
{
cfun->machine->eh_stack_adjust = EH_RETURN_STACKADJ_RTX;
mt_expand_epilogue (EH_EPILOGUE);
}
static tree
mt_handle_interrupt_attribute (tree * node,
tree name,
tree args ATTRIBUTE_UNUSED,
int flags ATTRIBUTE_UNUSED,
bool * no_add_attrs)
{
if (TREE_CODE (*node) != FUNCTION_DECL)
{
warning (OPT_Wattributes,
"%qs attribute only applies to functions",
IDENTIFIER_POINTER (name));
*no_add_attrs = true;
}
return NULL_TREE;
}
const struct attribute_spec mt_attribute_table[] =
{
{ "interrupt", 0, 0, false, false, false, mt_handle_interrupt_attribute },
{ NULL, 0, 0, false, false, false, NULL }
};
int
mt_initial_elimination_offset (int from, int to)
{
mt_compute_frame_size (get_frame_size ());
if (from == FRAME_POINTER_REGNUM && to == STACK_POINTER_REGNUM)
return 0;
else if (from == ARG_POINTER_REGNUM && to == STACK_POINTER_REGNUM)
return current_frame_info.total_size;
else if (from == ARG_POINTER_REGNUM && to == FRAME_POINTER_REGNUM)
return current_frame_info.total_size;
else
gcc_unreachable ();
}
static rtx
mt_generate_compare (enum rtx_code code, rtx op0, rtx op1)
{
rtx scratch0, scratch1, const_scratch;
switch (code)
{
case GTU:
case LTU:
case GEU:
case LEU:
scratch0 = gen_reg_rtx (SImode);
scratch1 = gen_reg_rtx (SImode);
const_scratch = force_reg (SImode, GEN_INT(MT_MIN_INT));
emit_insn (gen_addsi3 (scratch0, const_scratch, op0));
emit_insn (gen_addsi3 (scratch1, const_scratch, op1));
break;
default:
scratch0 = op0;
scratch1 = op1;
break;
}
switch (code)
{
case GTU:
code = GT; break;
case LTU:
code = LT; break;
case GEU:
code = GE; break;
case LEU:
code = LE; break;
default:
break;
}
return gen_rtx_fmt_ee (code, VOIDmode, scratch0, scratch1);
}
void
mt_emit_cbranch (enum rtx_code code, rtx loc, rtx op0, rtx op1)
{
rtx condition_rtx, loc_ref;
if (! reg_or_0_operand (op0, SImode))
op0 = copy_to_mode_reg (SImode, op0);
if (! reg_or_0_operand (op1, SImode))
op1 = copy_to_mode_reg (SImode, op1);
condition_rtx = mt_generate_compare (code, op0, op1);
loc_ref = gen_rtx_LABEL_REF (VOIDmode, loc);
emit_jump_insn (gen_rtx_SET (VOIDmode, pc_rtx,
gen_rtx_IF_THEN_ELSE (VOIDmode, condition_rtx,
loc_ref, pc_rtx)));
}
static void
mt_set_memflags_1 (rtx x, int in_struct_p, int volatile_p)
{
int i;
switch (GET_CODE (x))
{
case SEQUENCE:
case PARALLEL:
for (i = XVECLEN (x, 0) - 1; i >= 0; i--)
mt_set_memflags_1 (XVECEXP (x, 0, i), in_struct_p, volatile_p);
break;
case INSN:
mt_set_memflags_1 (PATTERN (x), in_struct_p, volatile_p);
break;
case SET:
mt_set_memflags_1 (SET_DEST (x), in_struct_p, volatile_p);
mt_set_memflags_1 (SET_SRC (x), in_struct_p, volatile_p);
break;
case MEM:
MEM_IN_STRUCT_P (x) = in_struct_p;
MEM_VOLATILE_P (x) = volatile_p;
break;
default:
break;
}
}
void
mt_set_memflags (rtx ref)
{
rtx insn;
int in_struct_p, volatile_p;
if (GET_CODE (ref) != MEM)
return;
in_struct_p = MEM_IN_STRUCT_P (ref);
volatile_p = MEM_VOLATILE_P (ref);
if (! in_struct_p && ! volatile_p)
return;
for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
mt_set_memflags_1 (insn, in_struct_p, volatile_p);
}
enum reg_class
mt_secondary_reload_class (enum reg_class class ATTRIBUTE_UNUSED,
enum machine_mode mode,
rtx x)
{
if ((mode == QImode && (!TARGET_BYTE_ACCESS)) || mode == HImode)
{
if (GET_CODE (x) == MEM
|| (GET_CODE (x) == REG && true_regnum (x) == -1)
|| (GET_CODE (x) == SUBREG
&& (GET_CODE (SUBREG_REG (x)) == MEM
|| (GET_CODE (SUBREG_REG (x)) == REG
&& true_regnum (SUBREG_REG (x)) == -1))))
return GENERAL_REGS;
}
return NO_REGS;
}
rtx
mt_function_value (tree valtype, enum machine_mode mode, tree func_decl ATTRIBUTE_UNUSED)
{
if ((mode) == DImode || (mode) == DFmode)
return gen_rtx_MEM (mode, gen_rtx_REG (mode, RETURN_VALUE_REGNUM));
if (valtype)
mode = TYPE_MODE (valtype);
return gen_rtx_REG (mode, RETURN_VALUE_REGNUM);
}
void
mt_split_words (enum machine_mode nmode,
enum machine_mode omode,
rtx *operands)
{
rtx dl,dh;
rtx sl,sh;
int move_high_first = 0;
switch (GET_CODE (operands[0]))
{
case SUBREG:
case REG:
if ((GET_CODE (operands[1]) == REG
|| GET_CODE (operands[1]) == SUBREG)
&& true_regnum (operands[0]) <= true_regnum (operands[1]))
move_high_first = 1;
if (GET_CODE (operands[0]) == SUBREG)
{
dl = gen_rtx_SUBREG (nmode, SUBREG_REG (operands[0]),
SUBREG_BYTE (operands[0]) + GET_MODE_SIZE (nmode));
dh = gen_rtx_SUBREG (nmode, SUBREG_REG (operands[0]), SUBREG_BYTE (operands[0]));
}
else if (GET_CODE (operands[0]) == REG && ! IS_PSEUDO_P (operands[0]))
{
int r = REGNO (operands[0]);
dh = gen_rtx_REG (nmode, r);
dl = gen_rtx_REG (nmode, r + HARD_REGNO_NREGS (r, nmode));
}
else
{
dh = gen_rtx_SUBREG (nmode, operands[0], 0);
dl = gen_rtx_SUBREG (nmode, operands[0], GET_MODE_SIZE (nmode));
}
break;
case MEM:
switch (GET_CODE (XEXP (operands[0], 0)))
{
case POST_INC:
case POST_DEC:
gcc_unreachable ();
default:
dl = operand_subword (operands[0],
GET_MODE_SIZE (nmode)/UNITS_PER_WORD,
0, omode);
dh = operand_subword (operands[0], 0, 0, omode);
}
break;
default:
gcc_unreachable ();
}
switch (GET_CODE (operands[1]))
{
case REG:
if (! IS_PSEUDO_P (operands[1]))
{
int r = REGNO (operands[1]);
sh = gen_rtx_REG (nmode, r);
sl = gen_rtx_REG (nmode, r + HARD_REGNO_NREGS (r, nmode));
}
else
{
sh = gen_rtx_SUBREG (nmode, operands[1], 0);
sl = gen_rtx_SUBREG (nmode, operands[1], GET_MODE_SIZE (nmode));
}
break;
case CONST_DOUBLE:
if (operands[1] == const0_rtx)
sh = sl = const0_rtx;
else
split_double (operands[1], & sh, & sl);
break;
case CONST_INT:
if (operands[1] == const0_rtx)
sh = sl = const0_rtx;
else
{
int vl, vh;
switch (nmode)
{
default:
gcc_unreachable ();
}
sl = GEN_INT (vl);
sh = GEN_INT (vh);
}
break;
case SUBREG:
sl = gen_rtx_SUBREG (nmode,
SUBREG_REG (operands[1]),
SUBREG_BYTE (operands[1]) + GET_MODE_SIZE (nmode));
sh = gen_rtx_SUBREG (nmode,
SUBREG_REG (operands[1]),
SUBREG_BYTE (operands[1]));
break;
case MEM:
switch (GET_CODE (XEXP (operands[1], 0)))
{
case POST_DEC:
case POST_INC:
gcc_unreachable ();
break;
default:
sl = operand_subword (operands[1],
GET_MODE_SIZE (nmode)/UNITS_PER_WORD,
0, omode);
sh = operand_subword (operands[1], 0, 0, omode);
if (GET_CODE (dl) == REG
&& true_regnum (dl)
== true_regnum (XEXP (XEXP (sl, 0 ), 0)))
move_high_first = 1;
}
break;
default:
gcc_unreachable ();
}
if (move_high_first)
{
operands[2] = dh;
operands[3] = sh;
operands[4] = dl;
operands[5] = sl;
}
else
{
operands[2] = dl;
operands[3] = sl;
operands[4] = dh;
operands[5] = sh;
}
return;
}
static bool
mt_pass_in_stack (enum machine_mode mode ATTRIBUTE_UNUSED, tree type)
{
return (((type) != 0
&& (TREE_CODE (TYPE_SIZE (type)) != INTEGER_CST
|| TREE_ADDRESSABLE (type))));
}
void mt_add_loop (void)
{
cfun->machine->has_loops++;
}
#define MAX_LOOP_DEPTH 4
#define MAX_LOOP_LENGTH (200 * 4)
typedef struct loop_info *loop_info;
DEF_VEC_P (loop_info);
DEF_VEC_ALLOC_P (loop_info,heap);
struct loop_info GTY (())
{
int loop_no;
basic_block predecessor;
basic_block head;
basic_block tail;
basic_block successor;
rtx dbnz;
rtx init;
rtx loop_init;
rtx loop_end;
rtx end_label;
int depth;
int length;
struct loop_info *next;
VEC (basic_block,heap) *blocks;
VEC (loop_info,heap) *loops;
};
typedef struct loop_work GTY(())
{
basic_block block;
loop_info loop;
} loop_work;
DEF_VEC_O (loop_work);
DEF_VEC_ALLOC_O (loop_work,heap);
static bool
mt_loop_nesting (loop_info loop)
{
loop_info inner;
unsigned ix;
int inner_depth = 0;
if (!loop->depth)
{
if (EDGE_COUNT (loop->head->preds) == 2)
{
loop->predecessor = EDGE_PRED (loop->head, 0)->src;
if (loop->predecessor == loop->tail)
loop->predecessor = EDGE_PRED (loop->head, 1)->src;
if (EDGE_COUNT (loop->predecessor->succs) != 1
|| !(EDGE_SUCC (loop->predecessor, 0)->flags & EDGE_FALLTHRU))
loop->predecessor = NULL;
}
loop->depth = -1;
if (loop->predecessor)
{
for (ix = 0; VEC_iterate (loop_info, loop->loops, ix++, inner);)
{
if (!inner->depth)
mt_loop_nesting (inner);
if (inner->depth < 0)
{
inner_depth = -1;
break;
}
if (inner_depth < inner->depth)
inner_depth = inner->depth;
loop->length += inner->length;
}
if (inner_depth >= 0)
loop->depth = inner_depth + 1;
}
}
return (loop->depth > 0
&& loop->predecessor
&& loop->depth < MAX_LOOP_DEPTH
&& loop->length < MAX_LOOP_LENGTH);
}
static int
mt_block_length (basic_block bb)
{
int length = 0;
rtx insn;
for (insn = BB_HEAD (bb);
insn != NEXT_INSN (BB_END (bb));
insn = NEXT_INSN (insn))
{
if (!INSN_P (insn))
continue;
if (CALL_P (insn))
{
length = MAX_LOOP_LENGTH + 1;
break;
}
length += get_attr_length (insn);
}
return length;
}
static bool
mt_scan_loop (loop_info loop, rtx reg, rtx dbnz)
{
unsigned ix;
loop_info inner;
basic_block bb;
for (ix = 0; VEC_iterate (basic_block, loop->blocks, ix, bb); ix++)
{
rtx insn;
for (insn = BB_HEAD (bb);
insn != NEXT_INSN (BB_END (bb));
insn = NEXT_INSN (insn))
{
if (!INSN_P (insn))
continue;
if (insn == dbnz)
continue;
if (reg_mentioned_p (reg, PATTERN (insn)))
return true;
}
}
for (ix = 0; VEC_iterate (loop_info, loop->loops, ix, inner); ix++)
if (mt_scan_loop (inner, reg, NULL_RTX))
return true;
return false;
}
static void
mt_reorg_loops (FILE *dump_file)
{
basic_block bb;
loop_info loops = NULL;
loop_info loop;
int nloops = 0;
unsigned dwork = 0;
VEC (loop_work,heap) *works = VEC_alloc (loop_work,heap,20);
loop_work *work;
edge e;
edge_iterator ei;
bool replaced = false;
FOR_EACH_BB (bb)
{
rtx tail = BB_END (bb);
while (GET_CODE (tail) == NOTE)
tail = PREV_INSN (tail);
bb->aux = NULL;
if (recog_memoized (tail) == CODE_FOR_decrement_and_branch_until_zero)
{
loop = XNEW (struct loop_info);
loop->next = loops;
loops = loop;
loop->tail = bb;
loop->head = BRANCH_EDGE (bb)->dest;
loop->successor = FALLTHRU_EDGE (bb)->dest;
loop->predecessor = NULL;
loop->dbnz = tail;
loop->depth = 0;
loop->length = mt_block_length (bb);
loop->blocks = VEC_alloc (basic_block, heap, 20);
VEC_quick_push (basic_block, loop->blocks, bb);
loop->loops = NULL;
loop->loop_no = nloops++;
loop->init = loop->end_label = NULL_RTX;
loop->loop_init = loop->loop_end = NULL_RTX;
work = VEC_safe_push (loop_work, heap, works, NULL);
work->block = loop->head;
work->loop = loop;
bb->aux = loop;
if (dump_file)
{
fprintf (dump_file, ";; potential loop %d ending at\n",
loop->loop_no);
print_rtl_single (dump_file, tail);
}
}
}
while (VEC_iterate (loop_work, works, dwork++, work))
{
loop = work->loop;
bb = work->block;
if (bb == EXIT_BLOCK_PTR)
loop->depth = -1;
else if (!bb->aux)
{
bb->aux = loop;
loop->length += mt_block_length (bb);
VEC_safe_push (basic_block, heap, loop->blocks, bb);
FOR_EACH_EDGE (e, ei, bb->succs)
{
if (!VEC_space (loop_work, works, 1))
{
if (dwork)
{
VEC_block_remove (loop_work, works, 0, dwork);
dwork = 0;
}
else
VEC_reserve (loop_work, heap, works, 1);
}
work = VEC_quick_push (loop_work, works, NULL);
work->block = EDGE_SUCC (bb, ei.index)->dest;
work->loop = loop;
}
}
else if (bb->aux != loop)
{
loop_info other = bb->aux;
if (other->head != bb)
loop->depth = -1;
else
{
VEC_safe_push (loop_info, heap, loop->loops, other);
work = VEC_safe_push (loop_work, heap, works, NULL);
work->loop = loop;
work->block = other->successor;
}
}
}
VEC_free (loop_work, heap, works);
for (loop = loops; loop; loop = loop->next)
{
rtx iter_reg, insn, init_insn;
rtx init_val, loop_end, loop_init, end_label, head_label;
if (!mt_loop_nesting (loop))
{
if (dump_file)
fprintf (dump_file, ";; loop %d is bad\n", loop->loop_no);
continue;
}
iter_reg = SET_DEST (XVECEXP (PATTERN (loop->dbnz), 0, 1));
if (!REG_P (iter_reg))
{
if (dump_file)
fprintf (dump_file, ";; loop %d has spilled iteration count\n",
loop->loop_no);
continue;
}
init_insn = NULL_RTX;
for (insn = BB_END (loop->predecessor);
insn != PREV_INSN (BB_HEAD (loop->predecessor));
insn = PREV_INSN (insn))
{
if (!INSN_P (insn))
continue;
if (reg_mentioned_p (iter_reg, PATTERN (insn)))
{
rtx set = single_set (insn);
if (set && rtx_equal_p (iter_reg, SET_DEST (set)))
init_insn = insn;
break;
}
}
if (!init_insn)
{
if (dump_file)
fprintf (dump_file, ";; loop %d has no initializer\n",
loop->loop_no);
continue;
}
if (dump_file)
{
fprintf (dump_file, ";; loop %d initialized by\n",
loop->loop_no);
print_rtl_single (dump_file, init_insn);
}
init_val = PATTERN (init_insn);
if (GET_CODE (init_val) == SET)
init_val = SET_SRC (init_val);
if (GET_CODE (init_val) != CONST_INT || INTVAL (init_val) >= 65535)
{
if (dump_file)
fprintf (dump_file, ";; loop %d has complex initializer\n",
loop->loop_no);
continue;
}
if (mt_scan_loop (loop, iter_reg, loop->dbnz))
{
if (dump_file)
fprintf (dump_file, ";; loop %d uses iterator\n",
loop->loop_no);
continue;
}
init_val = GEN_INT (INTVAL (init_val) + 1);
iter_reg = gen_rtx_REG (SImode, LOOP_FIRST + loop->depth - 1);
end_label = gen_label_rtx ();
head_label = XEXP (SET_SRC (XVECEXP (PATTERN (loop->dbnz), 0, 0)), 1);
loop_end = gen_loop_end (iter_reg, head_label);
loop_init = gen_loop_init (iter_reg, init_val, end_label);
loop->init = init_insn;
loop->end_label = end_label;
loop->loop_init = loop_init;
loop->loop_end = loop_end;
replaced = true;
if (dump_file)
{
fprintf (dump_file, ";; replacing loop %d initializer with\n",
loop->loop_no);
print_rtl_single (dump_file, loop->loop_init);
fprintf (dump_file, ";; replacing loop %d terminator with\n",
loop->loop_no);
print_rtl_single (dump_file, loop->loop_end);
}
}
for (loop = loops; loop; loop = loop->next)
if (loop->loop_init)
{
emit_jump_insn_after (loop->loop_init, BB_END (loop->predecessor));
delete_insn (loop->init);
emit_label_before (loop->end_label, loop->dbnz);
emit_jump_insn_before (loop->loop_end, loop->dbnz);
delete_insn (loop->dbnz);
}
while (loops)
{
loop = loops;
loops = loop->next;
VEC_free (loop_info, heap, loop->loops);
VEC_free (basic_block, heap, loop->blocks);
XDELETE (loop);
}
if (replaced && dump_file)
{
fprintf (dump_file, ";; Replaced loops\n");
print_rtl (dump_file, get_insns ());
}
}
typedef struct branch_info
{
rtx insn;
struct branch_info *next;
} branch_info;
typedef struct label_info
{
rtx label;
branch_info *branches;
struct label_info *next;
} label_info;
static label_info *mt_labels;
static int
mt_add_branches (rtx *x, void *insn)
{
if (GET_CODE (*x) == LABEL_REF)
{
branch_info *branch = xmalloc (sizeof (*branch));
rtx label = XEXP (*x, 0);
label_info *info;
for (info = mt_labels; info; info = info->next)
if (info->label == label)
break;
if (!info)
{
info = xmalloc (sizeof (*info));
info->next = mt_labels;
mt_labels = info;
info->label = label;
info->branches = NULL;
}
branch->next = info->branches;
info->branches = branch;
branch->insn = insn;
}
return 0;
}
static rtx
mt_check_delay_slot (rtx branch, rtx insn)
{
rtx slot;
rtx tmp;
rtx p;
rtx jmp;
gcc_assert (GET_CODE (PATTERN (branch)) == SEQUENCE);
if (INSN_DELETED_P (branch))
return NULL_RTX;
slot = XVECEXP (PATTERN (branch), 0, 1);
tmp = PATTERN (insn);
note_stores (PATTERN (slot), insn_dependent_p_1, &tmp);
if (tmp)
return NULL_RTX;
jmp = XVECEXP (PATTERN (branch), 0, 0);
tmp = PATTERN (jmp);
note_stores (PATTERN (slot), insn_dependent_p_1, &tmp);
if (!tmp)
return branch;
p = PREV_INSN (branch);
NEXT_INSN (p) = slot;
PREV_INSN (slot) = p;
NEXT_INSN (slot) = jmp;
PREV_INSN (jmp) = slot;
NEXT_INSN (jmp) = branch;
PREV_INSN (branch) = jmp;
XVECEXP (PATTERN (branch), 0, 0) = NULL_RTX;
XVECEXP (PATTERN (branch), 0, 1) = NULL_RTX;
delete_insn (branch);
return jmp;
}
static void
mt_reorg_hazard (void)
{
rtx insn, next;
for (insn = get_insns ();
insn;
insn = NEXT_INSN (insn))
{
rtx jmp;
if (!INSN_P (insn))
continue;
jmp = PATTERN (insn);
if (GET_CODE (jmp) != SEQUENCE)
continue;
jmp = XVECEXP (jmp, 0, 0);
if (recog_memoized (jmp) == CODE_FOR_tablejump)
for (jmp = XEXP (XEXP (XVECEXP (PATTERN (jmp), 0, 1), 0), 0);
!JUMP_TABLE_DATA_P (jmp);
jmp = NEXT_INSN (jmp))
continue;
for_each_rtx (&PATTERN (jmp), mt_add_branches, insn);
}
for (insn = get_insns ();
insn && !INSN_P (insn);
insn = NEXT_INSN (insn))
continue;
for (;
insn;
insn = next)
{
rtx jmp, tmp;
enum attr_type attr;
gcc_assert (INSN_P (insn) && !INSN_DELETED_P (insn));
for (next = NEXT_INSN (insn);
next;
next = NEXT_INSN (next))
{
if (!INSN_P (next))
continue;
if (GET_CODE (PATTERN (next)) != USE)
break;
}
jmp = insn;
if (GET_CODE (PATTERN (insn)) == SEQUENCE)
jmp = XVECEXP (PATTERN (insn), 0, 0);
attr = recog_memoized (jmp) >= 0 ? get_attr_type (jmp) : TYPE_UNKNOWN;
if (next && attr == TYPE_LOAD)
{
tmp = PATTERN (next);
if (GET_CODE (tmp) == SEQUENCE)
tmp = PATTERN (XVECEXP (tmp, 0, 0));
note_stores (PATTERN (insn), insn_dependent_p_1, &tmp);
if (!tmp)
emit_insn_after (gen_nop (), insn);
}
if (attr == TYPE_CALL)
{
int nops = 0;
int count;
rtx prev = insn;
rtx rescan = NULL_RTX;
for (count = 2; count && !nops;)
{
int type;
prev = PREV_INSN (prev);
if (!prev)
{
nops = count;
break;
}
if (BARRIER_P (prev))
break;
if (LABEL_P (prev))
{
label_info *label;
branch_info *branch;
for (label = mt_labels;
label;
label = label->next)
if (label->label == prev)
{
for (branch = label->branches;
branch;
branch = branch->next)
{
tmp = mt_check_delay_slot (branch->insn, jmp);
if (tmp == branch->insn)
{
nops = count;
break;
}
if (tmp && branch->insn == next)
rescan = tmp;
}
break;
}
continue;
}
if (!INSN_P (prev) || GET_CODE (PATTERN (prev)) == USE)
continue;
if (GET_CODE (PATTERN (prev)) == SEQUENCE)
{
tmp = mt_check_delay_slot (prev, jmp);
if (tmp == prev)
nops = count;
break;
}
type = (INSN_CODE (prev) >= 0 ? get_attr_type (prev)
: TYPE_COMPLEX);
if (type == TYPE_CALL || type == TYPE_BRANCH)
break;
if (type == TYPE_LOAD
|| type == TYPE_ARITH
|| type == TYPE_COMPLEX)
{
tmp = PATTERN (jmp);
note_stores (PATTERN (prev), insn_dependent_p_1, &tmp);
if (!tmp)
{
nops = count;
break;
}
}
if (INSN_CODE (prev) >= 0)
count--;
}
if (rescan)
for (next = NEXT_INSN (rescan);
next && !INSN_P (next);
next = NEXT_INSN (next))
continue;
while (nops--)
emit_insn_before (gen_nop (), insn);
}
}
while (mt_labels)
{
label_info *label = mt_labels;
branch_info *branch, *next;
mt_labels = label->next;
for (branch = label->branches; branch; branch = next)
{
next = branch->next;
free (branch);
}
free (label);
}
}
static void
mt_machine_reorg (void)
{
if (cfun->machine->has_loops && TARGET_MS2)
mt_reorg_loops (dump_file);
if (mt_flag_delayed_branch)
dbr_schedule (get_insns ());
if (TARGET_MS2)
{
split_all_insns_noflow ();
mt_reorg_hazard ();
}
}
const struct attribute_spec mt_attribute_table[];
#undef TARGET_ATTRIBUTE_TABLE
#define TARGET_ATTRIBUTE_TABLE mt_attribute_table
#undef TARGET_STRUCT_VALUE_RTX
#define TARGET_STRUCT_VALUE_RTX mt_struct_value_rtx
#undef TARGET_PROMOTE_PROTOTYPES
#define TARGET_PROMOTE_PROTOTYPES hook_bool_tree_true
#undef TARGET_PASS_BY_REFERENCE
#define TARGET_PASS_BY_REFERENCE mt_pass_by_reference
#undef TARGET_MUST_PASS_IN_STACK
#define TARGET_MUST_PASS_IN_STACK mt_pass_in_stack
#undef TARGET_ARG_PARTIAL_BYTES
#define TARGET_ARG_PARTIAL_BYTES mt_arg_partial_bytes
#undef TARGET_SETUP_INCOMING_VARARGS
#define TARGET_SETUP_INCOMING_VARARGS mt_setup_incoming_varargs
#undef TARGET_MACHINE_DEPENDENT_REORG
#define TARGET_MACHINE_DEPENDENT_REORG mt_machine_reorg
struct gcc_target targetm = TARGET_INITIALIZER;
#include "gt-mt.h"