#include "config.h"
#include "system.h"
#include "rtl.h"
#include "tree.h"
#include "tm_p.h"
#include "assert.h"
#include "mcore.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 "obstack.h"
#include "expr.h"
#include "reload.h"
#include "recog.h"
#include "function.h"
#include "ggc.h"
#include "toplev.h"
#include "target.h"
#include "target-def.h"
const char * mcore_stack_increment_string = 0;
int mcore_stack_increment = STACK_UNITS_MAXSTEP;
char * mcore_current_function_name = 0;
long mcore_current_compilation_timestamp = 0;
rtx arch_compare_op0;
rtx arch_compare_op1;
int regno_reg_class[FIRST_PSEUDO_REGISTER] =
{
GENERAL_REGS, ONLYR1_REGS, LRW_REGS, LRW_REGS,
LRW_REGS, LRW_REGS, LRW_REGS, LRW_REGS,
LRW_REGS, LRW_REGS, LRW_REGS, LRW_REGS,
LRW_REGS, LRW_REGS, LRW_REGS, GENERAL_REGS,
GENERAL_REGS, C_REGS, NO_REGS, NO_REGS,
};
const enum reg_class reg_class_from_letter[] =
{
LRW_REGS, ONLYR1_REGS, C_REGS, NO_REGS,
NO_REGS, NO_REGS, NO_REGS, NO_REGS,
NO_REGS, NO_REGS, NO_REGS, NO_REGS,
NO_REGS, NO_REGS, NO_REGS, NO_REGS,
NO_REGS, GENERAL_REGS, NO_REGS, NO_REGS,
NO_REGS, NO_REGS, NO_REGS, ALL_REGS,
NO_REGS, NO_REGS
};
struct mcore_frame
{
int arg_size;
int reg_size;
int reg_mask;
int local_size;
int outbound_size;
int pad_outbound;
int pad_local;
int pad_reg;
#define MAX_STACK_GROWS 4
int growth[MAX_STACK_GROWS];
int arg_offset;
int reg_offset;
int reg_growth;
int local_growth;
};
typedef enum
{
COND_NO,
COND_MOV_INSN,
COND_CLR_INSN,
COND_INC_INSN,
COND_DEC_INSN,
COND_BRANCH_INSN
}
cond_type;
static void output_stack_adjust PARAMS ((int, int));
static int calc_live_regs PARAMS ((int *));
static int const_ok_for_mcore PARAMS ((int));
static int try_constant_tricks PARAMS ((long, int *, int *));
static const char * output_inline_const PARAMS ((enum machine_mode, rtx *));
static void block_move_sequence PARAMS ((rtx, rtx, rtx, rtx, int, int, int));
static void layout_mcore_frame PARAMS ((struct mcore_frame *));
static cond_type is_cond_candidate PARAMS ((rtx));
static rtx emit_new_cond_insn PARAMS ((rtx, int));
static rtx conditionalize_block PARAMS ((rtx));
static void conditionalize_optimization PARAMS ((rtx));
static void mcore_add_gc_roots PARAMS ((void));
static rtx handle_structs_in_regs PARAMS ((enum machine_mode, tree, int));
static void mcore_mark_dllexport PARAMS ((tree));
static void mcore_mark_dllimport PARAMS ((tree));
static int mcore_dllexport_p PARAMS ((tree));
static int mcore_dllimport_p PARAMS ((tree));
const struct attribute_spec mcore_attribute_table[];
static tree mcore_handle_naked_attribute PARAMS ((tree *, tree, tree, int, bool *));
#ifdef OBJECT_FORMAT_ELF
static void mcore_asm_named_section PARAMS ((const char *,
unsigned int));
#endif
#ifdef TARGET_DLLIMPORT_DECL_ATTRIBUTES
#undef TARGET_MERGE_DECL_ATTRIBUTES
#define TARGET_MERGE_DECL_ATTRIBUTES merge_dllimport_decl_attributes
#endif
#ifdef OBJECT_FORMAT_ELF
#undef TARGET_ASM_UNALIGNED_HI_OP
#define TARGET_ASM_UNALIGNED_HI_OP "\t.short\t"
#undef TARGET_ASM_UNALIGNED_SI_OP
#define TARGET_ASM_UNALIGNED_SI_OP "\t.long\t"
#endif
#undef TARGET_ATTRIBUTE_TABLE
#define TARGET_ATTRIBUTE_TABLE mcore_attribute_table
struct gcc_target targetm = TARGET_INITIALIZER;
static void
output_stack_adjust (direction, size)
int direction;
int size;
{
if (direction < 0 && size > mcore_stack_increment && mcore_stack_increment > 0)
{
rtx tmp = gen_rtx (REG, SImode, 1);
rtx memref;
emit_insn (gen_movsi (tmp, GEN_INT (mcore_stack_increment)));
do
{
emit_insn (gen_subsi3 (stack_pointer_rtx, stack_pointer_rtx, tmp));
memref = gen_rtx (MEM, SImode, stack_pointer_rtx);
MEM_VOLATILE_P (memref) = 1;
emit_insn (gen_movsi (memref, stack_pointer_rtx));
size -= mcore_stack_increment;
}
while (size > mcore_stack_increment);
}
if (size)
{
rtx insn;
rtx val = GEN_INT (size);
if (size > 32)
{
rtx nval = gen_rtx (REG, SImode, 1);
emit_insn (gen_movsi (nval, val));
val = nval;
}
if (direction > 0)
insn = gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx, val);
else
insn = gen_subsi3 (stack_pointer_rtx, stack_pointer_rtx, val);
emit_insn (insn);
}
}
static int
calc_live_regs (count)
int * count;
{
int reg;
int live_regs_mask = 0;
* count = 0;
for (reg = 0; reg < FIRST_PSEUDO_REGISTER; reg++)
{
if (regs_ever_live[reg] && !call_used_regs[reg])
{
(*count)++;
live_regs_mask |= (1 << reg);
}
}
return live_regs_mask;
}
void
mcore_print_operand_address (stream, x)
FILE * stream;
rtx x;
{
switch (GET_CODE (x))
{
case REG:
fprintf (stream, "(%s)", reg_names[REGNO (x)]);
break;
case PLUS:
{
rtx base = XEXP (x, 0);
rtx index = XEXP (x, 1);
if (GET_CODE (base) != REG)
{
rtx temp = base;
base = index;
index = temp;
}
switch (GET_CODE (index))
{
case CONST_INT:
fprintf (stream, "(%s,%d)", reg_names[REGNO(base)],
INTVAL (index));
break;
default:
debug_rtx (x);
abort ();
}
}
break;
default:
output_addr_const (stream, x);
break;
}
}
void
mcore_print_operand (stream, x, code)
FILE * stream;
rtx x;
int code;
{
switch (code)
{
case 'N':
if (INTVAL(x) == -1)
fprintf (asm_out_file, "32");
else
fprintf (asm_out_file, "%d", exact_log2 (INTVAL (x) + 1));
break;
case 'P':
fprintf (asm_out_file, "%d", exact_log2 (INTVAL (x)));
break;
case 'Q':
fprintf (asm_out_file, "%d", exact_log2 (~INTVAL (x)));
break;
case 'O':
fprintf (asm_out_file, "%d", INTVAL (x));
break;
case 'M':
fprintf (asm_out_file, "%d", - INTVAL (x));
break;
case 'R':
switch (GET_CODE (x))
{
case REG:
fputs (reg_names[REGNO (x) + 1], (stream));
break;
case MEM:
mcore_print_operand_address
(stream, XEXP (adjust_address (x, SImode, 4), 0));
break;
default:
abort ();
}
break;
case 'U':
fprintf (asm_out_file, "%s-%s", reg_names[REGNO (x)],
reg_names[REGNO (x) + 3]);
break;
case 'x':
fprintf (asm_out_file, "0x%x", INTVAL (x));
break;
case 'X':
fprintf (asm_out_file, "%d", 3 - INTVAL (x) / 8);
break;
default:
switch (GET_CODE (x))
{
case REG:
fputs (reg_names[REGNO (x)], (stream));
break;
case MEM:
output_address (XEXP (x, 0));
break;
default:
output_addr_const (stream, x);
break;
}
break;
}
}
int
mcore_const_costs (exp, code)
rtx exp;
enum rtx_code code;
{
int val = INTVAL (exp);
if ( CONST_OK_FOR_I (val)
|| CONST_OK_FOR_M (val)
|| CONST_OK_FOR_N (val)
|| (code == PLUS && CONST_OK_FOR_L (val)))
return 1;
else if (code == AND
&& ( CONST_OK_FOR_M (~val)
|| CONST_OK_FOR_N (~val)))
return 2;
else if (code == PLUS
&& ( CONST_OK_FOR_I (-val)
|| CONST_OK_FOR_M (-val)
|| CONST_OK_FOR_N (-val)))
return 2;
return 5;
}
int
mcore_and_cost (x)
rtx x;
{
int val;
if (GET_CODE (XEXP (x, 1)) != CONST_INT)
return 2;
val = INTVAL (XEXP (x, 1));
if (CONST_OK_FOR_K (val) || CONST_OK_FOR_M (~val))
return 2;
else if (const_ok_for_mcore (val))
return 3;
else if (TARGET_HARDLIT && mcore_const_ok_for_inline (val))
return 4;
return 5;
}
int
mcore_ior_cost (x)
rtx x;
{
int val;
if (GET_CODE (XEXP (x, 1)) != CONST_INT)
return 2;
val = INTVAL (XEXP (x, 1));
if (CONST_OK_FOR_M (val))
return 2;
else if (const_ok_for_mcore (val))
return 3;
else if (TARGET_HARDLIT && mcore_const_ok_for_inline (val))
return 4;
return 5;
}
int
mcore_modify_comparison (code)
enum rtx_code code;
{
rtx op1 = arch_compare_op1;
if (GET_CODE (op1) == CONST_INT)
{
int val = INTVAL (op1);
switch (code)
{
case LE:
if (CONST_OK_FOR_J (val + 1))
{
arch_compare_op1 = GEN_INT (val + 1);
return 1;
}
break;
default:
break;
}
}
return 0;
}
rtx
mcore_gen_compare_reg (code)
enum rtx_code code;
{
rtx op0 = arch_compare_op0;
rtx op1 = arch_compare_op1;
rtx cc_reg = gen_rtx (REG, CCmode, CC_REG);
if (CONSTANT_P (op1) && GET_CODE (op1) != CONST_INT)
op1 = force_reg (SImode, op1);
switch (code)
{
case EQ:
code = NE;
case NE:
if (GET_CODE (op1) == CONST_INT && ! CONST_OK_FOR_K (INTVAL (op1)))
op1 = force_reg (SImode, op1);
break;
case LE:
code = GT;
case GT:
if (GET_CODE (op1) == CONST_INT)
op1 = force_reg (SImode, op1);
break;
case GE:
code = LT;
case LT:
if (GET_CODE (op1) == CONST_INT &&
INTVAL (op1) != 0 &&
! CONST_OK_FOR_J (INTVAL (op1)))
op1 = force_reg (SImode, op1);
break;
case GTU:
if (GET_CODE (op1) == CONST_INT && INTVAL (op1) == 0)
{
abort ();
break;
}
code = LEU;
case LEU:
if (GET_CODE (op1) == CONST_INT && INTVAL (op1) != 0)
op1 = force_reg (SImode, op1);
break;
case LTU:
code = GEU;
case GEU:
if (GET_CODE (op1) == CONST_INT && INTVAL (op1) != 0)
op1 = force_reg (SImode, op1);
break;
default:
break;
}
emit_insn (gen_rtx (SET, VOIDmode, cc_reg, gen_rtx (code, CCmode, op0, op1)));
return cc_reg;
}
int
mcore_symbolic_address_p (x)
rtx x;
{
switch (GET_CODE (x))
{
case SYMBOL_REF:
case LABEL_REF:
return 1;
case CONST:
x = XEXP (x, 0);
return ( (GET_CODE (XEXP (x, 0)) == SYMBOL_REF
|| GET_CODE (XEXP (x, 0)) == LABEL_REF)
&& GET_CODE (XEXP (x, 1)) == CONST_INT);
default:
return 0;
}
}
int
mcore_call_address_operand (x, mode)
rtx x;
enum machine_mode mode;
{
return register_operand (x, mode) || CONSTANT_P (x);
}
char *
mcore_output_call (operands, index)
rtx operands[];
int index;
{
static char buffer[20];
rtx addr = operands [index];
if (REG_P (addr))
{
if (TARGET_CG_DATA)
{
if (mcore_current_function_name == 0)
abort ();
ASM_OUTPUT_CG_EDGE (asm_out_file, mcore_current_function_name,
"unknown", 1);
}
sprintf (buffer, "jsr\t%%%d", index);
}
else
{
if (TARGET_CG_DATA)
{
if (mcore_current_function_name == 0)
abort ();
if (GET_CODE (addr) != SYMBOL_REF)
abort ();
ASM_OUTPUT_CG_EDGE (asm_out_file, mcore_current_function_name, XSTR (addr, 0), 0);
}
sprintf (buffer, "jbsr\t%%%d", index);
}
return buffer;
}
static int
const_ok_for_mcore (value)
int value;
{
if (value >= 0 && value <= 127)
return 1;
if ((value & (value - 1)) == 0)
return 1;
if ((value & (value + 1)) == 0)
return 1;
return 0;
}
int
mcore_const_ok_for_inline (value)
long value;
{
int x, y;
return try_constant_tricks (value, & x, & y) > 0;
}
int
mcore_const_trick_uses_not (value)
long value;
{
int x, y;
return try_constant_tricks (value, & x, & y) == 2;
}
static int
try_constant_tricks (value, x, y)
long value;
int * x;
int * y;
{
int i;
unsigned bit, shf, rot;
if (const_ok_for_mcore (value))
return 1;
if (TARGET_HARDLIT)
{
if (const_ok_for_mcore (~value))
{
*x = ~value;
return 2;
}
for (i = 1; i <= 32; i++)
{
if (const_ok_for_mcore (value - i))
{
*x = value - i;
*y = i;
return 3;
}
if (const_ok_for_mcore (value + i))
{
*x = value + i;
*y = i;
return 4;
}
}
bit = 0x80000000L;
for (i = 0; i <= 31; i++)
{
if (const_ok_for_mcore (i - value))
{
*x = i - value;
*y = i;
return 5;
}
if (const_ok_for_mcore (value & ~bit))
{
*y = bit;
*x = value & ~bit;
return 6;
}
if (const_ok_for_mcore (value | bit))
{
*y = ~bit;
*x = value | bit;
return 7;
}
bit >>= 1;
}
shf = value;
rot = value;
for (i = 1; i < 31; i++)
{
int c;
c = rot << 31;
rot >>= 1;
rot &= 0x7FFFFFFF;
rot |= c;
if (const_ok_for_mcore (rot))
{
*y = i;
*x = rot;
return 8;
}
if (shf & 1)
shf = 0;
shf >>= 1;
if (shf != 0 && const_ok_for_mcore (shf))
{
*y = i;
*x = shf;
return 9;
}
}
if ((value % 3) == 0 && const_ok_for_mcore (value / 3))
{
*x = value / 3;
return 10;
}
if ((value % 5) == 0 && const_ok_for_mcore (value / 5))
{
*x = value / 5;
return 11;
}
}
return 0;
}
int
mcore_is_dead (first, reg)
rtx first;
rtx reg;
{
rtx insn;
if (GET_CODE (reg) == SUBREG)
reg = SUBREG_REG (reg);
if (dead_or_set_p (first, reg))
return 1;
for (insn = NEXT_INSN (first); insn; insn = NEXT_INSN (insn))
{
if (GET_CODE (insn) == JUMP_INSN)
return 0;
else if (GET_CODE(insn) == CALL_INSN)
{
if (reg_referenced_p (reg, PATTERN (insn))
|| find_reg_fusage (insn, USE, reg))
return 0;
else if (dead_or_set_p (insn, reg))
return 1;
}
else if (GET_CODE (insn) == INSN)
{
if (reg_referenced_p (reg, PATTERN (insn)))
return 0;
else if (dead_or_set_p (insn, reg))
return 1;
}
}
return 0;
}
int
mcore_num_ones (mask)
int mask;
{
mask = (mask >> 1 & 0x55555555) + (mask & 0x55555555);
mask = ((mask >> 2) & 0x33333333) + (mask & 0x33333333);
mask = ((mask >> 4) + mask) & 0x0f0f0f0f;
mask = ((mask >> 8) + mask);
return (mask + (mask >> 16)) & 0xff;
}
int
mcore_num_zeros (mask)
int mask;
{
return 32 - mcore_num_ones (mask);
}
int
mcore_byte_offset (mask)
unsigned int mask;
{
if (mask == 0x00ffffffL)
return 0;
else if (mask == 0xff00ffffL)
return 1;
else if (mask == 0xffff00ffL)
return 2;
else if (mask == 0xffffff00L)
return 3;
return -1;
}
int
mcore_halfword_offset (mask)
unsigned int mask;
{
if (mask == 0x0000ffffL)
return 0;
else if (mask == 0xffff0000L)
return 1;
return -1;
}
const char *
mcore_output_bseti (dst, mask)
rtx dst;
int mask;
{
rtx out_operands[2];
int bit;
out_operands[0] = dst;
for (bit = 0; bit < 32; bit++)
{
if ((mask & 0x1) == 0x1)
{
out_operands[1] = GEN_INT (bit);
output_asm_insn ("bseti\t%0,%1", out_operands);
}
mask >>= 1;
}
return "";
}
const char *
mcore_output_bclri (dst, mask)
rtx dst;
int mask;
{
rtx out_operands[2];
int bit;
out_operands[0] = dst;
for (bit = 0; bit < 32; bit++)
{
if ((mask & 0x1) == 0x0)
{
out_operands[1] = GEN_INT (bit);
output_asm_insn ("bclri\t%0,%1", out_operands);
}
mask >>= 1;
}
return "";
}
const char *
mcore_output_cmov (operands, cmp_t, test)
rtx operands[];
int cmp_t;
const char * test;
{
int load_value;
int adjust_value;
rtx out_operands[4];
out_operands[0] = operands[0];
if (const_ok_for_mcore (INTVAL (operands[1])))
{
out_operands[1] = operands[1];
out_operands[2] = operands[2];
}
else if (const_ok_for_mcore (INTVAL (operands[2])))
{
out_operands[1] = operands[2];
out_operands[2] = operands[1];
cmp_t = (cmp_t == 0);
}
load_value = INTVAL (out_operands[1]);
adjust_value = INTVAL (out_operands[2]);
if (test)
output_asm_insn (test, operands);
if (load_value >= 0 && load_value <= 127)
output_asm_insn ("movi\t%0,%1", out_operands);
else if ((load_value & (load_value - 1)) == 0)
output_asm_insn ("bgeni\t%0,%P1", out_operands);
else if ((load_value & (load_value + 1)) == 0)
output_asm_insn ("bmaski\t%0,%N1", out_operands);
if (load_value > adjust_value)
{
if (cmp_t)
output_asm_insn ("decf\t%0", out_operands);
else
output_asm_insn ("dect\t%0", out_operands);
}
else
{
if (cmp_t)
output_asm_insn ("incf\t%0", out_operands);
else
output_asm_insn ("inct\t%0", out_operands);
}
return "";
}
const char *
mcore_output_andn (insn, operands)
rtx insn ATTRIBUTE_UNUSED;
rtx operands[];
{
int x, y;
rtx out_operands[3];
const char * load_op;
char buf[256];
if (try_constant_tricks (INTVAL (operands[1]), &x, &y) != 2)
abort ();
out_operands[0] = operands[0];
out_operands[1] = GEN_INT(x);
out_operands[2] = operands[2];
if (x >= 0 && x <= 127)
load_op = "movi\t%0,%1";
else if ((x & (x - 1)) == 0)
load_op = "bgeni\t%0,%P1";
else if ((x & (x + 1)) == 0)
load_op = "bmaski\t%0,%N1";
else
load_op = "BADMOVI\t%0,%1";
sprintf (buf, "%s\n\tandn\t%%2,%%0", load_op);
output_asm_insn (buf, out_operands);
return "";
}
static const char *
output_inline_const (mode, operands)
enum machine_mode mode;
rtx operands[];
{
int x = 0, y = 0;
int trick_no;
rtx out_operands[3];
char buf[256];
char load_op[256];
const char *dst_fmt;
int value;
value = INTVAL (operands[1]);
if ((trick_no = try_constant_tricks (value, &x, &y)) == 0)
{
abort ();
}
if (trick_no == 1)
x = value;
out_operands[0] = operands[0];
out_operands[1] = GEN_INT (x);
if (trick_no > 2)
out_operands[2] = GEN_INT (y);
if (mode == DImode && (! TARGET_LITTLE_END))
dst_fmt = "%R0";
else
dst_fmt = "%0";
if (x >= 0 && x <= 127)
sprintf (load_op, "movi\t%s,%%1", dst_fmt);
else if ((x & (x - 1)) == 0)
sprintf (load_op, "bgeni\t%s,%%P1", dst_fmt);
else if ((x & (x + 1)) == 0)
sprintf (load_op, "bmaski\t%s,%%N1", dst_fmt);
else
sprintf (load_op, "BADMOVI\t%s,%%1", dst_fmt);
switch (trick_no)
{
case 1:
strcpy (buf, load_op);
break;
case 2:
sprintf (buf, "%s\n\tnot\t%s\t// %d 0x%x", load_op, dst_fmt, value, value);
break;
case 3:
sprintf (buf, "%s\n\taddi\t%s,%%2\t// %d 0x%x", load_op, dst_fmt, value, value);
break;
case 4:
sprintf (buf, "%s\n\tsubi\t%s,%%2\t// %d 0x%x", load_op, dst_fmt, value, value);
break;
case 5:
sprintf (buf, "%s\n\trsubi\t%s,%%2\t// %d 0x%x", load_op, dst_fmt, value, value);
break;
case 6:
sprintf (buf, "%s\n\tbseti\t%s,%%P2\t// %d 0x%x", load_op, dst_fmt, value, value);
break;
case 7:
sprintf (buf, "%s\n\tbclri\t%s,%%Q2\t// %d 0x%x", load_op, dst_fmt, value, value);
break;
case 8:
sprintf (buf, "%s\n\trotli\t%s,%%2\t// %d 0x%x", load_op, dst_fmt, value, value);
break;
case 9:
sprintf (buf, "%s\n\tlsli\t%s,%%2\t// %d 0x%x", load_op, dst_fmt, value, value);
break;
case 10:
sprintf (buf, "%s\n\tixh\t%s,%s\t// %d 0x%x", load_op, dst_fmt, dst_fmt, value, value);
break;
case 11:
sprintf (buf, "%s\n\tixw\t%s,%s\t// %d 0x%x", load_op, dst_fmt, dst_fmt, value, value);
break;
default:
return "";
}
output_asm_insn (buf, out_operands);
return "";
}
const char *
mcore_output_move (insn, operands, mode)
rtx insn ATTRIBUTE_UNUSED;
rtx operands[];
enum machine_mode mode ATTRIBUTE_UNUSED;
{
rtx dst = operands[0];
rtx src = operands[1];
if (GET_CODE (dst) == REG)
{
if (GET_CODE (src) == REG)
{
if (REGNO (src) == CC_REG)
return "mvc\t%0";
else
return "mov\t%0,%1";
}
else if (GET_CODE (src) == MEM)
{
if (GET_CODE (XEXP (src, 0)) == LABEL_REF)
return "lrw\t%0,[%1]";
else
return "ldw\t%0,%1";
}
else if (GET_CODE (src) == CONST_INT)
{
int x, y;
if (CONST_OK_FOR_I (INTVAL (src)))
return "movi\t%0,%1";
else if (CONST_OK_FOR_M (INTVAL (src)))
return "bgeni\t%0,%P1\t// %1 %x1";
else if (CONST_OK_FOR_N (INTVAL (src)))
return "bmaski\t%0,%N1\t// %1 %x1";
else if (try_constant_tricks (INTVAL (src), &x, &y))
return output_inline_const (SImode, operands);
else
return "lrw\t%0,%x1\t// %1";
}
else
return "lrw\t%0, %1";
}
else if (GET_CODE (dst) == MEM)
return "stw\t%1,%0";
abort ();
}
const char *
mcore_output_inline_const_forced (insn, operands, mode)
rtx insn ATTRIBUTE_UNUSED;
rtx operands[];
enum machine_mode mode ATTRIBUTE_UNUSED;
{
unsigned long value = INTVAL (operands[1]);
unsigned long ovalue = value;
struct piece
{
int low;
int shift;
}
part[6];
int i;
if (mcore_const_ok_for_inline (value))
return output_inline_const (SImode, operands);
for (i = 0; (unsigned) i < ARRAY_SIZE (part); i++)
{
part[i].shift = 0;
part[i].low = (value & 0x1F);
value -= part[i].low;
if (mcore_const_ok_for_inline (value))
break;
else
{
value >>= 5;
part[i].shift = 5;
while ((value & 1) == 0)
{
part[i].shift++;
value >>= 1;
}
if (mcore_const_ok_for_inline (value))
break;
}
}
if (value == 0 || ! mcore_const_ok_for_inline (value))
abort ();
operands[1] = GEN_INT (value);
output_asm_insn (output_inline_const (SImode, operands), operands);
while (i >= 0)
{
if (part[i].shift)
{
operands[2] = GEN_INT (part[i].shift);
output_asm_insn ("lsli %0,%2", operands);
value <<= part[i].shift;
}
if (part[i].low != 0)
{
operands[2] = GEN_INT (part[i].low);
output_asm_insn ("addi %0,%2", operands);
value += part[i].low;
}
i--;
}
if (value != ovalue)
abort ();
return "";
}
const char *
mcore_output_movedouble (operands, mode)
rtx operands[];
enum machine_mode mode ATTRIBUTE_UNUSED;
{
rtx dst = operands[0];
rtx src = operands[1];
if (GET_CODE (dst) == REG)
{
if (GET_CODE (src) == REG)
{
int dstreg = REGNO (dst);
int srcreg = REGNO (src);
if (srcreg + 1 == dstreg)
return "mov %R0,%R1\n\tmov %0,%1";
else
return "mov %0,%1\n\tmov %R0,%R1";
}
else if (GET_CODE (src) == MEM)
{
rtx memexp = memexp = XEXP (src, 0);
int dstreg = REGNO (dst);
int basereg = -1;
if (GET_CODE (memexp) == LABEL_REF)
return "lrw\t%0,[%1]\n\tlrw\t%R0,[%R1]";
else if (GET_CODE (memexp) == REG)
basereg = REGNO (memexp);
else if (GET_CODE (memexp) == PLUS)
{
if (GET_CODE (XEXP (memexp, 0)) == REG)
basereg = REGNO (XEXP (memexp, 0));
else if (GET_CODE (XEXP (memexp, 1)) == REG)
basereg = REGNO (XEXP (memexp, 1));
else
abort ();
}
else
abort ();
if (dstreg == basereg)
{
return "ldw\t%R0,%R1\n\tldw\t%0,%1";
}
else
return "ldw\t%0,%1\n\tldw\t%R0,%R1";
}
else if (GET_CODE (src) == CONST_INT)
{
if (TARGET_LITTLE_END)
{
if (CONST_OK_FOR_I (INTVAL (src)))
output_asm_insn ("movi %0,%1", operands);
else if (CONST_OK_FOR_M (INTVAL (src)))
output_asm_insn ("bgeni %0,%P1", operands);
else if (INTVAL (src) == -1)
output_asm_insn ("bmaski %0,32", operands);
else if (CONST_OK_FOR_N (INTVAL (src)))
output_asm_insn ("bmaski %0,%N1", operands);
else
abort ();
if (INTVAL (src) < 0)
return "bmaski %R0,32";
else
return "movi %R0,0";
}
else
{
if (CONST_OK_FOR_I (INTVAL (src)))
output_asm_insn ("movi %R0,%1", operands);
else if (CONST_OK_FOR_M (INTVAL (src)))
output_asm_insn ("bgeni %R0,%P1", operands);
else if (INTVAL (src) == -1)
output_asm_insn ("bmaski %R0,32", operands);
else if (CONST_OK_FOR_N (INTVAL (src)))
output_asm_insn ("bmaski %R0,%N1", operands);
else
abort ();
if (INTVAL (src) < 0)
return "bmaski %0,32";
else
return "movi %0,0";
}
}
else
abort ();
}
else if (GET_CODE (dst) == MEM && GET_CODE (src) == REG)
return "stw\t%1,%0\n\tstw\t%R1,%R0";
else
abort ();
}
int
mcore_general_movsrc_operand (op, mode)
rtx op;
enum machine_mode mode;
{
if (GET_CODE (op) == MEM && GET_CODE (XEXP (op, 0)) == LABEL_REF)
return 1;
return general_operand (op, mode);
}
int
mcore_general_movdst_operand (op, mode)
rtx op;
enum machine_mode mode;
{
if (GET_CODE (op) == REG && REGNO (op) == CC_REG)
return 0;
return general_operand (op, mode);
}
int
mcore_arith_reg_operand (op, mode)
rtx op;
enum machine_mode mode;
{
if (! register_operand (op, mode))
return 0;
if (GET_CODE (op) == SUBREG)
op = SUBREG_REG (op);
if (GET_CODE (op) == REG)
return REGNO (op) != CC_REG;
return 1;
}
int
mcore_reload_operand (op, mode)
rtx op;
enum machine_mode mode;
{
if (mcore_arith_reg_operand (op, mode))
return 1;
if (! reload_in_progress)
return 0;
return GET_CODE (op) == MEM;
}
int
mcore_arith_J_operand (op, mode)
rtx op;
enum machine_mode mode;
{
if (register_operand (op, mode))
return 1;
if (GET_CODE (op) == CONST_INT && CONST_OK_FOR_J (INTVAL (op)))
return 1;
return 0;
}
int
mcore_arith_K_operand (op, mode)
rtx op;
enum machine_mode mode;
{
if (register_operand (op, mode))
return 1;
if (GET_CODE (op) == CONST_INT && CONST_OK_FOR_K (INTVAL (op)))
return 1;
return 0;
}
int
mcore_arith_K_operand_not_0 (op, mode)
rtx op;
enum machine_mode mode;
{
if (register_operand (op, mode))
return 1;
if ( GET_CODE (op) == CONST_INT
&& CONST_OK_FOR_K (INTVAL (op))
&& INTVAL (op) != 0)
return 1;
return 0;
}
int
mcore_arith_K_S_operand (op, mode)
rtx op;
enum machine_mode mode;
{
if (register_operand (op, mode))
return 1;
if (GET_CODE (op) == CONST_INT)
{
if (CONST_OK_FOR_K (INTVAL (op)) || CONST_OK_FOR_M (~INTVAL (op)))
return 1;
}
return 0;
}
int
mcore_arith_S_operand (op)
rtx op;
{
if (GET_CODE (op) == CONST_INT && CONST_OK_FOR_M (~INTVAL (op)))
return 1;
return 0;
}
int
mcore_arith_M_operand (op, mode)
rtx op;
enum machine_mode mode;
{
if (register_operand (op, mode))
return 1;
if (GET_CODE (op) == CONST_INT && CONST_OK_FOR_M (INTVAL (op)))
return 1;
return 0;
}
int
mcore_arith_imm_operand (op, mode)
rtx op;
enum machine_mode mode;
{
if (register_operand (op, mode))
return 1;
if (GET_CODE (op) == CONST_INT && const_ok_for_mcore (INTVAL (op)))
return 1;
return 0;
}
int
mcore_arith_any_imm_operand (op, mode)
rtx op;
enum machine_mode mode;
{
if (register_operand (op, mode))
return 1;
if (GET_CODE (op) == CONST_INT)
return 1;
return 0;
}
int
mcore_arith_O_operand (op, mode)
rtx op;
enum machine_mode mode;
{
if (register_operand (op, mode))
return 1;
if (GET_CODE (op) == CONST_INT && CONST_OK_FOR_O (INTVAL (op)))
return 1;
return 0;
}
int
mcore_literal_K_operand (op, mode)
rtx op;
enum machine_mode mode ATTRIBUTE_UNUSED;
{
if (GET_CODE (op) == CONST_INT && CONST_OK_FOR_K (INTVAL (op)))
return 1;
return 0;
}
int
mcore_addsub_operand (op, mode)
rtx op;
enum machine_mode mode;
{
if (register_operand (op, mode))
return 1;
if (GET_CODE (op) == CONST_INT)
{
return 1;
}
return 0;
}
int
mcore_compare_operand (op, mode)
rtx op;
enum machine_mode mode;
{
if (register_operand (op, mode))
return 1;
if (GET_CODE (op) == CONST_INT && INTVAL (op) == 0)
return 1;
return 0;
}
int
mcore_expand_insv (operands)
rtx operands[];
{
int width = INTVAL (operands[1]);
int posn = INTVAL (operands[2]);
int mask;
rtx mreg, sreg, ereg;
if (width == 1 && GET_CODE (operands[3]) == CONST_INT)
{
if ((INTVAL(operands[3])&1) == 0)
{
mask = ~(1 << posn);
emit_insn (gen_rtx (SET, SImode, operands[0],
gen_rtx (AND, SImode, operands[0], GEN_INT (mask))));
}
else
{
mask = 1 << posn;
emit_insn (gen_rtx (SET, SImode, operands[0],
gen_rtx (IOR, SImode, operands[0], GEN_INT (mask))));
}
return 1;
}
if (! TARGET_W_FIELD)
return 0;
if (width == 8 && posn % 8 == 0)
return 0;
if (width == 16 && posn % 16 == 0)
return 0;
if (GET_CODE (operands[3]) == CONST_INT &&
INTVAL (operands[3]) == ((1 << width) - 1))
{
mreg = force_reg (SImode, GEN_INT (INTVAL (operands[3]) << posn));
emit_insn (gen_rtx (SET, SImode, operands[0],
gen_rtx (IOR, SImode, operands[0], mreg)));
return 1;
}
mreg = force_reg (SImode, GEN_INT (~(((1 << width) - 1) << posn)));
emit_insn (gen_rtx (SET, SImode, operands[0],
gen_rtx (AND, SImode, operands[0], mreg)));
if (GET_CODE (operands[3]) == CONST_INT && INTVAL (operands[3]) == 0)
return 1;
sreg = copy_to_mode_reg (SImode, operands[3]);
if (width + posn != (int) GET_MODE_SIZE (SImode))
{
ereg = force_reg (SImode, GEN_INT ((1 << width) - 1));
emit_insn (gen_rtx (SET, SImode, sreg,
gen_rtx (AND, SImode, sreg, ereg)));
}
if (posn != 0)
emit_insn (gen_rtx (SET, SImode, sreg,
gen_rtx (ASHIFT, SImode, sreg, GEN_INT (posn))));
emit_insn (gen_rtx (SET, SImode, operands[0],
gen_rtx (IOR, SImode, operands[0], sreg)));
return 1;
}
int
mcore_load_multiple_operation (op, mode)
rtx op;
enum machine_mode mode ATTRIBUTE_UNUSED;
{
int count = XVECLEN (op, 0);
int dest_regno;
rtx src_addr;
int i;
if (count <= 1
|| GET_CODE (XVECEXP (op, 0, 0)) != SET
|| GET_CODE (SET_DEST (XVECEXP (op, 0, 0))) != REG
|| GET_CODE (SET_SRC (XVECEXP (op, 0, 0))) != MEM)
return 0;
dest_regno = REGNO (SET_DEST (XVECEXP (op, 0, 0)));
src_addr = XEXP (SET_SRC (XVECEXP (op, 0, 0)), 0);
for (i = 1; i < count; i++)
{
rtx elt = XVECEXP (op, 0, i);
if (GET_CODE (elt) != SET
|| GET_CODE (SET_DEST (elt)) != REG
|| GET_MODE (SET_DEST (elt)) != SImode
|| REGNO (SET_DEST (elt)) != (unsigned) (dest_regno + i)
|| GET_CODE (SET_SRC (elt)) != MEM
|| GET_MODE (SET_SRC (elt)) != SImode
|| GET_CODE (XEXP (SET_SRC (elt), 0)) != PLUS
|| ! rtx_equal_p (XEXP (XEXP (SET_SRC (elt), 0), 0), src_addr)
|| GET_CODE (XEXP (XEXP (SET_SRC (elt), 0), 1)) != CONST_INT
|| INTVAL (XEXP (XEXP (SET_SRC (elt), 0), 1)) != i * 4)
return 0;
}
return 1;
}
int
mcore_store_multiple_operation (op, mode)
rtx op;
enum machine_mode mode ATTRIBUTE_UNUSED;
{
int count = XVECLEN (op, 0);
int src_regno;
rtx dest_addr;
int i;
if (count <= 1
|| GET_CODE (XVECEXP (op, 0, 0)) != SET
|| GET_CODE (SET_DEST (XVECEXP (op, 0, 0))) != MEM
|| GET_CODE (SET_SRC (XVECEXP (op, 0, 0))) != REG)
return 0;
src_regno = REGNO (SET_SRC (XVECEXP (op, 0, 0)));
dest_addr = XEXP (SET_DEST (XVECEXP (op, 0, 0)), 0);
for (i = 1; i < count; i++)
{
rtx elt = XVECEXP (op, 0, i);
if (GET_CODE (elt) != SET
|| GET_CODE (SET_SRC (elt)) != REG
|| GET_MODE (SET_SRC (elt)) != SImode
|| REGNO (SET_SRC (elt)) != (unsigned) (src_regno + i)
|| GET_CODE (SET_DEST (elt)) != MEM
|| GET_MODE (SET_DEST (elt)) != SImode
|| GET_CODE (XEXP (SET_DEST (elt), 0)) != PLUS
|| ! rtx_equal_p (XEXP (XEXP (SET_DEST (elt), 0), 0), dest_addr)
|| GET_CODE (XEXP (XEXP (SET_DEST (elt), 0), 1)) != CONST_INT
|| INTVAL (XEXP (XEXP (SET_DEST (elt), 0), 1)) != i * 4)
return 0;
}
return 1;
}
static const enum machine_mode mode_from_align[] =
{
VOIDmode, QImode, HImode, VOIDmode, SImode,
VOIDmode, VOIDmode, VOIDmode, DImode
};
static void
block_move_sequence (dest, dst_mem, src, src_mem, size, align, offset)
rtx dest, dst_mem;
rtx src, src_mem;
int size;
int align;
int offset;
{
rtx temp[2];
enum machine_mode mode[2];
int amount[2];
int active[2];
int phase = 0;
int next;
int offset_ld = offset;
int offset_st = offset;
active[0] = active[1] = FALSE;
amount[0] = amount[1] = align;
mode[0] = mode_from_align[align];
temp[0] = gen_reg_rtx (mode[0]);
if (size >= 2 * align)
{
mode[1] = mode[0];
temp[1] = gen_reg_rtx (mode[1]);
}
do
{
rtx srcp, dstp;
next = phase;
phase = !phase;
if (size > 0)
{
if (size < amount[next])
{
amount[next] = (size >= 4 ? 4 : (size >= 2 ? 2 : 1));
mode[next] = mode_from_align[amount[next]];
temp[next] = gen_reg_rtx (mode[next]);
}
size -= amount[next];
srcp = gen_rtx (MEM,
#if 0
MEM_IN_STRUCT_P (src_mem) ? mode[next] : BLKmode,
#else
mode[next],
#endif
gen_rtx (PLUS, Pmode, src,
gen_rtx (CONST_INT, SImode, offset_ld)));
RTX_UNCHANGING_P (srcp) = RTX_UNCHANGING_P (src_mem);
MEM_VOLATILE_P (srcp) = MEM_VOLATILE_P (src_mem);
MEM_IN_STRUCT_P (srcp) = 1;
emit_insn (gen_rtx (SET, VOIDmode, temp[next], srcp));
offset_ld += amount[next];
active[next] = TRUE;
}
if (active[phase])
{
active[phase] = FALSE;
dstp = gen_rtx (MEM,
#if 0
MEM_IN_STRUCT_P (dst_mem) ? mode[phase] : BLKmode,
#else
mode[phase],
#endif
gen_rtx (PLUS, Pmode, dest,
gen_rtx (CONST_INT, SImode, offset_st)));
RTX_UNCHANGING_P (dstp) = RTX_UNCHANGING_P (dst_mem);
MEM_VOLATILE_P (dstp) = MEM_VOLATILE_P (dst_mem);
MEM_IN_STRUCT_P (dstp) = 1;
emit_insn (gen_rtx (SET, VOIDmode, dstp, temp[phase]));
offset_st += amount[phase];
}
}
while (active[next]);
}
void
mcore_expand_block_move (dst_mem, src_mem, operands)
rtx dst_mem;
rtx src_mem;
rtx * operands;
{
int align = INTVAL (operands[3]);
int bytes;
if (GET_CODE (operands[2]) == CONST_INT)
{
bytes = INTVAL (operands[2]);
if (bytes <= 0)
return;
if (align > 4)
align = 4;
if ((align == 4 && (bytes <= 4*4
|| ((bytes & 01) == 0 && bytes <= 8*4)
|| ((bytes & 03) == 0 && bytes <= 16*4)))
|| (align == 2 && bytes <= 4*2)
|| (align == 1 && bytes <= 4*1))
{
block_move_sequence (operands[0], dst_mem, operands[1], src_mem,
bytes, align, 0);
return;
}
}
emit_library_call (gen_rtx (SYMBOL_REF, Pmode, "memcpy"), 0, VOIDmode, 3,
operands[0], Pmode, operands[1], Pmode, operands[2],
SImode);
}
static int number_of_regs_before_varargs;
static int current_function_anonymous_args;
#define STACK_BYTES (STACK_BOUNDARY/BITS_PER_UNIT)
#define STORE_REACH (64)
#define ADDI_REACH (32)
static void
layout_mcore_frame (infp)
struct mcore_frame * infp;
{
int n;
unsigned int i;
int nbytes;
int regarg;
int localregarg;
int localreg;
int outbounds;
unsigned int growths;
int step;
nbytes = current_function_pretend_args_size;
if (current_function_anonymous_args)
nbytes += (NPARM_REGS - number_of_regs_before_varargs) * UNITS_PER_WORD;
infp->arg_size = nbytes;
infp->reg_mask = calc_live_regs (& n);
infp->reg_size = n * 4;
infp->local_size = get_frame_size ();
infp->outbound_size = current_function_outgoing_args_size;
if (infp->local_size % STACK_BYTES)
infp->local_size = (infp->local_size + STACK_BYTES - 1) & ~ (STACK_BYTES -1);
infp->pad_local = 0;
infp->pad_reg = 0;
infp->pad_outbound = 0;
if (infp->outbound_size % STACK_BYTES)
infp->pad_outbound = STACK_BYTES - (infp->outbound_size % STACK_BYTES);
for (i = 0; i < ARRAY_SIZE (infp->growth); i++)
infp->growth[i] = 0;
regarg = infp->reg_size + infp->arg_size;
localregarg = infp->local_size + regarg;
localreg = infp->local_size + infp->reg_size;
outbounds = infp->outbound_size + infp->pad_outbound;
growths = 0;
if (localregarg <= ADDI_REACH
&& (infp->reg_size <= 8 || (infp->reg_mask & 0xc000) != 0xc000))
{
if (localregarg % STACK_BYTES)
infp->pad_reg = STACK_BYTES - (localregarg % STACK_BYTES);
step = localregarg + infp->pad_reg;
infp->reg_offset = infp->local_size;
if (outbounds + step <= ADDI_REACH && !frame_pointer_needed)
{
step += outbounds;
infp->reg_offset += outbounds;
outbounds = 0;
}
infp->arg_offset = step - 4;
infp->growth[growths++] = step;
infp->reg_growth = growths;
infp->local_growth = growths;
if (outbounds)
infp->growth[growths++] = outbounds;
goto finish;
}
if (localregarg <= STORE_REACH
&& (infp->local_size > ADDI_REACH)
&& (infp->reg_size <= 8 || (infp->reg_mask & 0xc000) != 0xc000))
{
int all;
if (localregarg % STACK_BYTES)
infp->pad_reg = STACK_BYTES - (localregarg % STACK_BYTES);
all = localregarg + infp->pad_reg + infp->pad_local;
step = ADDI_REACH;
if (step > all)
step = all;
infp->arg_offset = step - 4;
infp->growth[growths++] = step;
infp->reg_growth = growths;
infp->reg_offset = step - infp->pad_reg - infp->reg_size;
all -= step;
if (outbounds + all <= ADDI_REACH && !frame_pointer_needed)
{
all += outbounds;
outbounds = 0;
}
step = all;
infp->growth[growths++] = step;
infp->local_growth = growths;
all -= step;
assert (all == 0);
if (outbounds)
infp->growth[growths++] = outbounds;
goto finish;
}
if ((regarg % STACK_BYTES) == 0)
{
infp->growth[growths++] = regarg;
infp->reg_growth = growths;
infp->arg_offset = regarg - 4;
infp->reg_offset = 0;
if (infp->local_size % STACK_BYTES)
infp->pad_local = STACK_BYTES - (infp->local_size % STACK_BYTES);
step = infp->local_size + infp->pad_local;
if (!frame_pointer_needed)
{
step += outbounds;
outbounds = 0;
}
infp->growth[growths++] = step;
infp->local_growth = growths;
if (outbounds)
infp->growth[growths++] = outbounds;
goto finish;
}
if (regarg % STACK_BYTES != 0)
infp->pad_reg = STACK_BYTES - (regarg % STACK_BYTES);
infp->growth[growths++] = infp->arg_size + infp->reg_size + infp->pad_reg;
infp->reg_growth = growths;
infp->arg_offset = infp->growth[0] - 4;
infp->reg_offset = 0;
if (frame_pointer_needed)
{
if (infp->local_size % STACK_BYTES != 0)
infp->pad_local = STACK_BYTES - (infp->local_size % STACK_BYTES);
infp->growth[growths++] = infp->local_size + infp->pad_local;
infp->local_growth = growths;
infp->growth[growths++] = outbounds;
}
else
{
if ((infp->local_size + outbounds) % STACK_BYTES != 0)
infp->pad_local = STACK_BYTES - ((infp->local_size + outbounds) % STACK_BYTES);
infp->growth[growths++] = infp->local_size + infp->pad_local + outbounds;
infp->local_growth = growths;
}
finish:
assert (infp->reg_offset >= 0);
assert (growths <= MAX_STACK_GROWS);
for (i = 0; i < growths; i++)
{
if (infp->growth[i] % STACK_BYTES)
{
fprintf (stderr,"stack growth of %d is not %d aligned\n",
infp->growth[i], STACK_BYTES);
abort ();
}
}
}
int
mcore_initial_elimination_offset (from, to)
int from;
int to;
{
int above_frame;
int below_frame;
struct mcore_frame fi;
layout_mcore_frame (& fi);
above_frame = fi.local_size + fi.pad_local + fi.reg_size + fi.pad_reg;
below_frame = fi.outbound_size + fi.pad_outbound;
if (from == ARG_POINTER_REGNUM && to == FRAME_POINTER_REGNUM)
return above_frame;
if (from == ARG_POINTER_REGNUM && to == STACK_POINTER_REGNUM)
return above_frame + below_frame;
if (from == FRAME_POINTER_REGNUM && to == STACK_POINTER_REGNUM)
return below_frame;
abort ();
return 0;
}
void
mcore_setup_incoming_varargs (args_so_far, mode, type, ptr_pretend_size)
CUMULATIVE_ARGS args_so_far;
enum machine_mode mode;
tree type;
int * ptr_pretend_size ATTRIBUTE_UNUSED;
{
current_function_anonymous_args = 1;
number_of_regs_before_varargs = args_so_far + mcore_num_arg_regs (mode, type);
number_of_regs_before_varargs = args_so_far;
if (number_of_regs_before_varargs > NPARM_REGS)
number_of_regs_before_varargs = NPARM_REGS;
}
void
mcore_expand_prolog ()
{
struct mcore_frame fi;
int space_allocated = 0;
int growth = 0;
layout_mcore_frame (&fi);
space_allocated = fi.arg_size + fi.reg_size + fi.local_size +
fi.outbound_size + fi.pad_outbound + fi.pad_local + fi.pad_reg;
if (TARGET_CG_DATA)
{
rtx x;
int len;
x = DECL_RTL (current_function_decl);
if (GET_CODE (x) != MEM)
abort ();
x = XEXP (x, 0);
if (GET_CODE (x) != SYMBOL_REF)
abort ();
if (mcore_current_function_name)
free (mcore_current_function_name);
len = strlen (XSTR (x, 0)) + 1;
mcore_current_function_name = (char *) xmalloc (len);
memcpy (mcore_current_function_name, XSTR (x, 0), len);
ASM_OUTPUT_CG_NODE (asm_out_file, mcore_current_function_name, space_allocated);
if (current_function_calls_alloca)
ASM_OUTPUT_CG_EDGE (asm_out_file, mcore_current_function_name, "alloca", 1);
fprintf (asm_out_file,
"\t.equ\t__$frame$info$_%s_$_%d_%d_x%x_%d_%d_%d,0\n",
mcore_current_function_name,
fi.arg_size, fi.reg_size, fi.reg_mask,
fi.local_size, fi.outbound_size,
frame_pointer_needed);
}
if (mcore_naked_function_p ())
return;
output_stack_adjust (-1, fi.growth[growth++]);
if (fi.arg_size != 0 && current_function_pretend_args_size == 0)
{
int offset;
int rn = FIRST_PARM_REG + NPARM_REGS - 1;
int remaining = fi.arg_size;
for (offset = fi.arg_offset; remaining >= 4; offset -= 4, rn--, remaining -= 4)
{
emit_insn (gen_movsi
(gen_rtx (MEM, SImode,
plus_constant (stack_pointer_rtx, offset)),
gen_rtx (REG, SImode, rn)));
}
}
if (growth < fi.reg_growth)
output_stack_adjust (-1, fi.growth[growth++]);
if (fi.reg_size != 0)
{
int i;
int offs = fi.reg_offset;
for (i = 15; i >= 0; i--)
{
if (offs == 0 && i == 15 && ((fi.reg_mask & 0xc000) == 0xc000))
{
int first_reg = 15;
while (fi.reg_mask & (1 << first_reg))
first_reg--;
first_reg++;
emit_insn (gen_store_multiple (gen_rtx (MEM, SImode, stack_pointer_rtx),
gen_rtx (REG, SImode, first_reg),
GEN_INT (16 - first_reg)));
i -= (15 - first_reg);
offs += (16 - first_reg) * 4;
}
else if (fi.reg_mask & (1 << i))
{
emit_insn (gen_movsi
(gen_rtx (MEM, SImode,
plus_constant (stack_pointer_rtx, offs)),
gen_rtx (REG, SImode, i)));
offs += 4;
}
}
}
if (frame_pointer_needed)
{
if (growth < fi.local_growth)
output_stack_adjust (-1, fi.growth[growth++]);
emit_insn (gen_movsi (frame_pointer_rtx, stack_pointer_rtx));
if (fi.growth[growth])
output_stack_adjust (-1, fi.growth[growth++]);
}
else
{
if (growth < fi.local_growth)
output_stack_adjust (-1, fi.growth[growth++]);
if (fi.growth[growth])
output_stack_adjust (-1, fi.growth[growth++]);
}
}
void
mcore_expand_epilog ()
{
struct mcore_frame fi;
int i;
int offs;
int growth = MAX_STACK_GROWS - 1 ;
layout_mcore_frame(&fi);
if (mcore_naked_function_p ())
return;
if (frame_pointer_needed)
{
emit_insn (gen_movsi (stack_pointer_rtx, frame_pointer_rtx));
growth = fi.local_growth - 1;
}
else
{
while (growth >= fi.local_growth)
{
if (fi.growth[growth] != 0)
output_stack_adjust (1, fi.growth[growth]);
growth--;
}
}
while (growth >= fi.reg_growth)
output_stack_adjust ( 1, fi.growth[growth--]);
offs = fi.reg_offset;
for (i = 15; i >= 0; i--)
{
if (offs == 0 && i == 15 && ((fi.reg_mask & 0xc000) == 0xc000))
{
int first_reg;
first_reg = 15;
while (fi.reg_mask & (1 << first_reg))
first_reg--;
first_reg++;
emit_insn (gen_load_multiple (gen_rtx (REG, SImode, first_reg),
gen_rtx (MEM, SImode, stack_pointer_rtx),
GEN_INT (16 - first_reg)));
i -= (15 - first_reg);
offs += (16 - first_reg) * 4;
}
else if (fi.reg_mask & (1 << i))
{
emit_insn (gen_movsi
(gen_rtx (REG, SImode, i),
gen_rtx (MEM, SImode,
plus_constant (stack_pointer_rtx, offs))));
offs += 4;
}
}
while (growth >= 0)
output_stack_adjust ( 1, fi.growth[growth--]);
}
typedef struct
{
rtx value;
rtx label;
} pool_node;
#define MAX_COUNT 1016
#define MAX_POOL_SIZE (MAX_COUNT/4)
static pool_node pool_vector[MAX_POOL_SIZE];
static int pool_size;
const char *
mcore_output_jump_label_table ()
{
int i;
if (pool_size)
{
fprintf (asm_out_file, "\t.align 2\n");
for (i = 0; i < pool_size; i++)
{
pool_node * p = pool_vector + i;
ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "L", CODE_LABEL_NUMBER (p->label));
output_asm_insn (".long %0", &p->value);
}
pool_size = 0;
}
return "";
}
static cond_type
is_cond_candidate (insn)
rtx insn;
{
if (GET_CODE (insn) == INSN)
{
rtx pat = PATTERN (insn);
rtx src, dst;
if (GET_CODE (pat) != SET)
return COND_NO;
dst = XEXP (pat, 0);
if ((GET_CODE (dst) != REG &&
GET_CODE (dst) != SUBREG) ||
GET_MODE (dst) != SImode)
return COND_NO;
src = XEXP (pat, 1);
if ((GET_CODE (src) == REG ||
(GET_CODE (src) == SUBREG &&
GET_CODE (SUBREG_REG (src)) == REG)) &&
GET_MODE (src) == SImode)
return COND_MOV_INSN;
else if (GET_CODE (src) == CONST_INT &&
INTVAL (src) == 0)
return COND_CLR_INSN;
else if (GET_CODE (src) == PLUS &&
(GET_CODE (XEXP (src, 0)) == REG ||
(GET_CODE (XEXP (src, 0)) == SUBREG &&
GET_CODE (SUBREG_REG (XEXP (src, 0))) == REG)) &&
GET_MODE (XEXP (src, 0)) == SImode &&
GET_CODE (XEXP (src, 1)) == CONST_INT &&
INTVAL (XEXP (src, 1)) == 1)
return COND_INC_INSN;
else if (((GET_CODE (src) == MINUS &&
GET_CODE (XEXP (src, 1)) == CONST_INT &&
INTVAL( XEXP (src, 1)) == 1) ||
(GET_CODE (src) == PLUS &&
GET_CODE (XEXP (src, 1)) == CONST_INT &&
INTVAL (XEXP (src, 1)) == -1)) &&
(GET_CODE (XEXP (src, 0)) == REG ||
(GET_CODE (XEXP (src, 0)) == SUBREG &&
GET_CODE (SUBREG_REG (XEXP (src, 0))) == REG)) &&
GET_MODE (XEXP (src, 0)) == SImode)
return COND_DEC_INSN;
}
else if (GET_CODE (insn) == JUMP_INSN &&
GET_CODE (PATTERN (insn)) == SET &&
GET_CODE (XEXP (PATTERN (insn), 1)) == LABEL_REF)
return COND_BRANCH_INSN;
return COND_NO;
}
static rtx
emit_new_cond_insn (insn, cond)
rtx insn;
int cond;
{
rtx c_insn = 0;
rtx pat, dst, src;
cond_type num;
if ((num = is_cond_candidate (insn)) == COND_NO)
return NULL;
pat = PATTERN (insn);
if (GET_CODE (insn) == INSN)
{
dst = SET_DEST (pat);
src = SET_SRC (pat);
}
else
{
dst = JUMP_LABEL (insn);
src = NULL_RTX;
}
switch (num)
{
case COND_MOV_INSN:
case COND_CLR_INSN:
if (cond)
c_insn = gen_movt0 (dst, src, dst);
else
c_insn = gen_movt0 (dst, dst, src);
break;
case COND_INC_INSN:
if (cond)
c_insn = gen_incscc (dst, dst);
else
c_insn = gen_incscc_false (dst, dst);
break;
case COND_DEC_INSN:
if (cond)
c_insn = gen_decscc (dst, dst);
else
c_insn = gen_decscc_false (dst, dst);
break;
case COND_BRANCH_INSN:
if (cond)
c_insn = gen_branch_true (dst);
else
c_insn = gen_branch_false (dst);
break;
default:
return NULL;
}
if (rtx_length [GET_CODE (c_insn)] >= 7 && rtx_length [GET_CODE (insn)] >= 7)
{
REG_NOTES (c_insn) = REG_NOTES (insn);
}
if (num == COND_BRANCH_INSN)
{
c_insn = emit_jump_insn_before (c_insn, insn);
++ LABEL_NUSES (dst);
JUMP_LABEL (c_insn) = dst;
}
else
c_insn = emit_insn_after (c_insn, insn);
delete_insn (insn);
return c_insn;
}
static rtx
conditionalize_block (first)
rtx first;
{
rtx insn;
rtx br_pat;
rtx end_blk_1_br = 0;
rtx end_blk_2_insn = 0;
rtx start_blk_3_lab = 0;
int cond;
int br_lab_num;
int blk_size = 0;
if (GET_CODE (first) != JUMP_INSN ||
GET_CODE (PATTERN (first)) != SET ||
GET_CODE (XEXP (PATTERN (first), 1)) != IF_THEN_ELSE)
return NEXT_INSN (first);
end_blk_1_br = first;
br_pat = PATTERN (end_blk_1_br);
cond = (GET_CODE (XEXP (XEXP (br_pat, 1), 0)) == EQ);
if (GET_CODE (XEXP (XEXP (br_pat, 1), 1)) == LABEL_REF)
{
br_lab_num = CODE_LABEL_NUMBER (XEXP (XEXP (XEXP (br_pat, 1), 1), 0));
}
else
{
cond = (cond == 0);
br_lab_num = CODE_LABEL_NUMBER (XEXP (XEXP (XEXP (br_pat, 1), 2), 0));
}
for (insn = NEXT_INSN (first); insn; insn = NEXT_INSN (insn))
{
enum rtx_code code;
code = GET_CODE (insn);
if (code == CODE_LABEL && CODE_LABEL_NUMBER (insn) == br_lab_num)
break;
if (code != BARRIER && code != NOTE && !is_cond_candidate (insn))
return NEXT_INSN (insn);
if (code == JUMP_INSN || code == INSN)
{
blk_size ++;
end_blk_2_insn = insn;
}
}
if (!insn)
return insn;
if (optimize > 1 && blk_size > 2)
return insn;
start_blk_3_lab = insn;
for (insn = NEXT_INSN (end_blk_1_br); insn != start_blk_3_lab;
insn = NEXT_INSN (insn))
{
rtx newinsn;
if (INSN_DELETED_P (insn))
continue;
if ((newinsn = emit_new_cond_insn (insn, cond)))
{
if (end_blk_2_insn == insn)
end_blk_2_insn = newinsn;
insn = newinsn;
}
}
if (LABEL_NUSES (start_blk_3_lab) == 1)
{
start_blk_3_lab = 0;
}
delete_insn (end_blk_1_br);
if (! start_blk_3_lab)
return end_blk_2_insn;
return NEXT_INSN (start_blk_3_lab);
}
static void
conditionalize_optimization (first)
rtx first;
{
rtx insn;
for (insn = first; insn; insn = conditionalize_block (insn))
continue;
}
static int saved_warn_return_type = -1;
static int saved_warn_return_type_count = 0;
void
mcore_dependent_reorg (first)
rtx first;
{
current_function_anonymous_args = 0;
if (saved_warn_return_type != -1)
{
if (--saved_warn_return_type_count == 0)
{
warn_return_type = saved_warn_return_type;
saved_warn_return_type = -1;
}
}
if (optimize == 0)
return;
conditionalize_optimization (first);
}
enum reg_class
mcore_reload_class (x, class)
rtx x;
enum reg_class class;
{
enum reg_class new_class;
if (class == GENERAL_REGS && CONSTANT_P (x)
&& (GET_CODE (x) != CONST_INT
|| ( ! CONST_OK_FOR_I (INTVAL (x))
&& ! CONST_OK_FOR_M (INTVAL (x))
&& ! CONST_OK_FOR_N (INTVAL (x)))))
new_class = LRW_REGS;
else
new_class = class;
return new_class;
}
int
mcore_is_same_reg (x, y)
rtx x;
rtx y;
{
while (GET_CODE (x) == SUBREG)
x = SUBREG_REG (x);
while (GET_CODE (y) == SUBREG)
y = SUBREG_REG (y);
if (GET_CODE(x) == REG && GET_CODE(y) == REG && REGNO(x) == REGNO(y))
return 1;
return 0;
}
static void
mcore_add_gc_roots ()
{
ggc_add_rtx_root (&arch_compare_op0, 1);
ggc_add_rtx_root (&arch_compare_op1, 1);
}
void
mcore_override_options ()
{
if (mcore_stack_increment_string)
{
mcore_stack_increment = atoi (mcore_stack_increment_string);
if (mcore_stack_increment < 0
|| (mcore_stack_increment == 0
&& (mcore_stack_increment_string[0] != '0'
|| mcore_stack_increment_string[1] != 0)))
error ("invalid option `-mstack-increment=%s'",
mcore_stack_increment_string);
}
if (TARGET_LITTLE_END && ! TARGET_M340)
target_flags |= M340_BIT;
mcore_add_gc_roots ();
}
int
mcore_must_pass_on_stack (mode, type)
enum machine_mode mode ATTRIBUTE_UNUSED;
tree type;
{
if (type == NULL)
return 0;
if (TREE_ADDRESSABLE (type))
return 1;
return 0;
}
int
mcore_num_arg_regs (mode, type)
enum machine_mode mode;
tree type;
{
int size;
if (MUST_PASS_IN_STACK (mode, type))
return 0;
if (type && mode == BLKmode)
size = int_size_in_bytes (type);
else
size = GET_MODE_SIZE (mode);
return ROUND_ADVANCE (size);
}
static rtx
handle_structs_in_regs (mode, type, reg)
enum machine_mode mode;
tree type;
int reg;
{
int size;
if (type
&& TYPE_MODE (type) == BLKmode
&& TREE_CODE (TYPE_SIZE (type)) == INTEGER_CST
&& (size = int_size_in_bytes (type)) > UNITS_PER_WORD
&& (size % UNITS_PER_WORD != 0)
&& (reg + mcore_num_arg_regs (mode, type) <= (FIRST_PARM_REG + NPARM_REGS)))
{
rtx arg_regs [NPARM_REGS];
int nregs;
rtx result;
rtvec rtvec;
for (nregs = 0; size > 0; size -= UNITS_PER_WORD)
{
arg_regs [nregs] =
gen_rtx_EXPR_LIST (SImode, gen_rtx_REG (SImode, reg ++),
GEN_INT (nregs * UNITS_PER_WORD));
nregs ++;
}
assert (ARRAY_SIZE (arg_regs) == 6);
rtvec = gen_rtvec (nregs, arg_regs[0], arg_regs[1], arg_regs[2],
arg_regs[3], arg_regs[4], arg_regs[5]);
result = gen_rtx_PARALLEL (mode, rtvec);
return result;
}
return gen_rtx_REG (mode, reg);
}
rtx
mcore_function_value (valtype, func)
tree valtype;
tree func ATTRIBUTE_UNUSED;
{
enum machine_mode mode;
int unsigned_p;
mode = TYPE_MODE (valtype);
PROMOTE_MODE (mode, unsigned_p, NULL);
return handle_structs_in_regs (mode, valtype, FIRST_RET_REG);
}
rtx
mcore_function_arg (cum, mode, type, named)
CUMULATIVE_ARGS cum;
enum machine_mode mode;
tree type;
int named;
{
int arg_reg;
if (! named)
return 0;
if (MUST_PASS_IN_STACK (mode, type))
return 0;
arg_reg = ROUND_REG (cum, mode);
if (arg_reg < NPARM_REGS)
return handle_structs_in_regs (mode, type, FIRST_PARM_REG + arg_reg);
return 0;
}
int
mcore_function_arg_partial_nregs (cum, mode, type, named)
CUMULATIVE_ARGS cum;
enum machine_mode mode;
tree type;
int named;
{
int reg = ROUND_REG (cum, mode);
if (named == 0)
return 0;
if (MUST_PASS_IN_STACK (mode, type))
return 0;
if (reg >= NPARM_REGS)
return 0;
if (reg + mcore_num_arg_regs (mode, type) <= NPARM_REGS)
return 0;
reg = NPARM_REGS - reg;
return reg;
}
int
mcore_dllexport_name_p (symbol)
const char * symbol;
{
return symbol[0] == '@' && symbol[1] == 'e' && symbol[2] == '.';
}
int
mcore_dllimport_name_p (symbol)
const char * symbol;
{
return symbol[0] == '@' && symbol[1] == 'i' && symbol[2] == '.';
}
static void
mcore_mark_dllexport (decl)
tree decl;
{
const char * oldname;
char * newname;
rtx rtlname;
tree idp;
rtlname = XEXP (DECL_RTL (decl), 0);
if (GET_CODE (rtlname) == SYMBOL_REF)
oldname = XSTR (rtlname, 0);
else if ( GET_CODE (rtlname) == MEM
&& GET_CODE (XEXP (rtlname, 0)) == SYMBOL_REF)
oldname = XSTR (XEXP (rtlname, 0), 0);
else
abort ();
if (mcore_dllexport_name_p (oldname))
return;
newname = alloca (strlen (oldname) + 4);
sprintf (newname, "@e.%s", oldname);
idp = get_identifier (newname);
XEXP (DECL_RTL (decl), 0) =
gen_rtx (SYMBOL_REF, Pmode, IDENTIFIER_POINTER (idp));
}
static void
mcore_mark_dllimport (decl)
tree decl;
{
const char * oldname;
char * newname;
tree idp;
rtx rtlname;
rtx newrtl;
rtlname = XEXP (DECL_RTL (decl), 0);
if (GET_CODE (rtlname) == SYMBOL_REF)
oldname = XSTR (rtlname, 0);
else if ( GET_CODE (rtlname) == MEM
&& GET_CODE (XEXP (rtlname, 0)) == SYMBOL_REF)
oldname = XSTR (XEXP (rtlname, 0), 0);
else
abort ();
if (mcore_dllexport_name_p (oldname))
abort ();
else if (mcore_dllimport_name_p (oldname))
return;
if (TREE_CODE (decl) == VAR_DECL
&& !DECL_VIRTUAL_P (decl)
&& DECL_INITIAL (decl))
{
error_with_decl (decl, "initialized variable `%s' is marked dllimport");
return;
}
if (TREE_CODE (decl) == VAR_DECL
&& !DECL_VIRTUAL_P (decl))
{
DECL_EXTERNAL (decl) = 1;
TREE_PUBLIC (decl) = 1;
}
newname = alloca (strlen (oldname) + 11);
sprintf (newname, "@i.__imp_%s", oldname);
idp = get_identifier (newname);
newrtl = gen_rtx (MEM, Pmode,
gen_rtx (SYMBOL_REF, Pmode,
IDENTIFIER_POINTER (idp)));
XEXP (DECL_RTL (decl), 0) = newrtl;
}
static int
mcore_dllexport_p (decl)
tree decl;
{
if ( TREE_CODE (decl) != VAR_DECL
&& TREE_CODE (decl) != FUNCTION_DECL)
return 0;
return lookup_attribute ("dllexport", DECL_ATTRIBUTES (decl)) != 0;
}
static int
mcore_dllimport_p (decl)
tree decl;
{
if ( TREE_CODE (decl) != VAR_DECL
&& TREE_CODE (decl) != FUNCTION_DECL)
return 0;
return lookup_attribute ("dllimport", DECL_ATTRIBUTES (decl)) != 0;
}
void
mcore_encode_section_info (decl)
tree decl;
{
if (optimize > 0
&& TREE_CONSTANT (decl)
&& (!flag_writable_strings || TREE_CODE (decl) != STRING_CST))
{
rtx rtl = (TREE_CODE_CLASS (TREE_CODE (decl)) != 'd'
? TREE_CST_RTL (decl) : DECL_RTL (decl));
SYMBOL_REF_FLAG (XEXP (rtl, 0)) = 1;
}
if (mcore_dllexport_p (decl))
mcore_mark_dllexport (decl);
else if (mcore_dllimport_p (decl))
mcore_mark_dllimport (decl);
else if ((TREE_CODE (decl) == FUNCTION_DECL
|| TREE_CODE (decl) == VAR_DECL)
&& DECL_RTL (decl) != NULL_RTX
&& GET_CODE (DECL_RTL (decl)) == MEM
&& GET_CODE (XEXP (DECL_RTL (decl), 0)) == MEM
&& GET_CODE (XEXP (XEXP (DECL_RTL (decl), 0), 0)) == SYMBOL_REF
&& mcore_dllimport_name_p (XSTR (XEXP (XEXP (DECL_RTL (decl), 0), 0), 0)))
{
const char * oldname = XSTR (XEXP (XEXP (DECL_RTL (decl), 0), 0), 0);
tree idp = get_identifier (oldname + 9);
rtx newrtl = gen_rtx (SYMBOL_REF, Pmode, IDENTIFIER_POINTER (idp));
XEXP (DECL_RTL (decl), 0) = newrtl;
}
}
const struct attribute_spec mcore_attribute_table[] =
{
{ "dllexport", 0, 0, true, false, false, NULL },
{ "dllimport", 0, 0, true, false, false, NULL },
{ "naked", 0, 0, true, false, false, mcore_handle_naked_attribute },
{ NULL, 0, 0, false, false, false, NULL }
};
static tree
mcore_handle_naked_attribute (node, name, args, flags, no_add_attrs)
tree *node;
tree name;
tree args ATTRIBUTE_UNUSED;
int flags ATTRIBUTE_UNUSED;
bool *no_add_attrs;
{
if (TREE_CODE (*node) == FUNCTION_DECL)
{
if (warn_return_type)
{
saved_warn_return_type = warn_return_type;
warn_return_type = 0;
saved_warn_return_type_count = 2;
}
else if (saved_warn_return_type_count)
saved_warn_return_type_count = 2;
}
else
{
warning ("`%s' attribute only applies to functions",
IDENTIFIER_POINTER (name));
*no_add_attrs = true;
}
return NULL_TREE;
}
void
mcore_unique_section (decl, reloc)
tree decl;
int reloc ATTRIBUTE_UNUSED;
{
int len;
char * name;
char * string;
const char * prefix;
name = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl));
STRIP_NAME_ENCODING (name, name);
if (TREE_CODE (decl) == FUNCTION_DECL)
prefix = ".text$";
else if (DECL_READONLY_SECTION (decl, 0))
prefix = ".rdata$";
else
prefix = ".data$";
len = strlen (name) + strlen (prefix);
string = alloca (len + 1);
sprintf (string, "%s%s", prefix, name);
DECL_SECTION_NAME (decl) = build_string (len, string);
}
int
mcore_naked_function_p ()
{
return lookup_attribute ("naked", DECL_ATTRIBUTES (current_function_decl)) != NULL_TREE;
}
#ifdef OBJECT_FORMAT_ELF
static void
mcore_asm_named_section (name, flags)
const char *name;
unsigned int flags ATTRIBUTE_UNUSED;
{
fprintf (asm_out_file, "\t.section %s\n", name);
}
#endif