#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "tm.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;
const 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 (int, int);
static int calc_live_regs (int *);
static int const_ok_for_mcore (int);
static int try_constant_tricks (long, int *, int *);
static const char * output_inline_const (enum machine_mode, rtx *);
static void layout_mcore_frame (struct mcore_frame *);
static void mcore_setup_incoming_varargs (CUMULATIVE_ARGS *, enum machine_mode, tree, int *, int);
static cond_type is_cond_candidate (rtx);
static rtx emit_new_cond_insn (rtx, int);
static rtx conditionalize_block (rtx);
static void conditionalize_optimization (void);
static void mcore_reorg (void);
static rtx handle_structs_in_regs (enum machine_mode, tree, int);
static void mcore_mark_dllexport (tree);
static void mcore_mark_dllimport (tree);
static int mcore_dllexport_p (tree);
static int mcore_dllimport_p (tree);
const struct attribute_spec mcore_attribute_table[];
static tree mcore_handle_naked_attribute (tree *, tree, tree, int, bool *);
#ifdef OBJECT_FORMAT_ELF
static void mcore_asm_named_section (const char *,
unsigned int, tree);
#endif
static void mcore_unique_section (tree, int);
static void mcore_encode_section_info (tree, rtx, int);
static const char *mcore_strip_name_encoding (const char *);
static int mcore_const_costs (rtx, RTX_CODE);
static int mcore_and_cost (rtx);
static int mcore_ior_cost (rtx);
static bool mcore_rtx_costs (rtx, int, int, int *);
static void mcore_external_libcall (rtx);
static bool mcore_return_in_memory (tree, tree);
static int mcore_arg_partial_bytes (CUMULATIVE_ARGS *,
enum machine_mode,
tree, bool);
#undef TARGET_ASM_EXTERNAL_LIBCALL
#define TARGET_ASM_EXTERNAL_LIBCALL mcore_external_libcall
#if 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
#undef TARGET_ASM_UNIQUE_SECTION
#define TARGET_ASM_UNIQUE_SECTION mcore_unique_section
#undef TARGET_ASM_FUNCTION_RODATA_SECTION
#define TARGET_ASM_FUNCTION_RODATA_SECTION default_no_function_rodata_section
#undef TARGET_ENCODE_SECTION_INFO
#define TARGET_ENCODE_SECTION_INFO mcore_encode_section_info
#undef TARGET_STRIP_NAME_ENCODING
#define TARGET_STRIP_NAME_ENCODING mcore_strip_name_encoding
#undef TARGET_RTX_COSTS
#define TARGET_RTX_COSTS mcore_rtx_costs
#undef TARGET_ADDRESS_COST
#define TARGET_ADDRESS_COST hook_int_rtx_0
#undef TARGET_MACHINE_DEPENDENT_REORG
#define TARGET_MACHINE_DEPENDENT_REORG mcore_reorg
#undef TARGET_PROMOTE_FUNCTION_ARGS
#define TARGET_PROMOTE_FUNCTION_ARGS hook_bool_tree_true
#undef TARGET_PROMOTE_FUNCTION_RETURN
#define TARGET_PROMOTE_FUNCTION_RETURN hook_bool_tree_true
#undef TARGET_PROMOTE_PROTOTYPES
#define TARGET_PROMOTE_PROTOTYPES hook_bool_tree_true
#undef TARGET_RETURN_IN_MEMORY
#define TARGET_RETURN_IN_MEMORY mcore_return_in_memory
#undef TARGET_MUST_PASS_IN_STACK
#define TARGET_MUST_PASS_IN_STACK must_pass_in_stack_var_size
#undef TARGET_PASS_BY_REFERENCE
#define TARGET_PASS_BY_REFERENCE hook_pass_by_reference_must_pass_in_stack
#undef TARGET_ARG_PARTIAL_BYTES
#define TARGET_ARG_PARTIAL_BYTES mcore_arg_partial_bytes
#undef TARGET_SETUP_INCOMING_VARARGS
#define TARGET_SETUP_INCOMING_VARARGS mcore_setup_incoming_varargs
struct gcc_target targetm = TARGET_INITIALIZER;
static void
output_stack_adjust (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 (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 (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," HOST_WIDE_INT_PRINT_DEC ")",
reg_names[REGNO(base)], INTVAL (index));
break;
default:
debug_rtx (x);
abort ();
}
}
break;
default:
output_addr_const (stream, x);
break;
}
}
void
mcore_print_operand (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, HOST_WIDE_INT_PRINT_DEC, INTVAL (x));
break;
case 'M':
fprintf (asm_out_file, HOST_WIDE_INT_PRINT_DEC, - 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, HOST_WIDE_INT_PRINT_HEX, INTVAL (x));
break;
case 'X':
fprintf (asm_out_file, HOST_WIDE_INT_PRINT_DEC, 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;
}
}
static int
mcore_const_costs (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;
}
static int
mcore_and_cost (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;
}
static int
mcore_ior_cost (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;
}
static bool
mcore_rtx_costs (rtx x, int code, int outer_code, int * total)
{
switch (code)
{
case CONST_INT:
*total = mcore_const_costs (x, outer_code);
return true;
case CONST:
case LABEL_REF:
case SYMBOL_REF:
*total = 5;
return true;
case CONST_DOUBLE:
*total = 10;
return true;
case AND:
*total = COSTS_N_INSNS (mcore_and_cost (x));
return true;
case IOR:
*total = COSTS_N_INSNS (mcore_ior_cost (x));
return true;
case DIV:
case UDIV:
case MOD:
case UMOD:
case FLOAT:
case FIX:
*total = COSTS_N_INSNS (100);
return true;
default:
return false;
}
}
int
mcore_modify_comparison (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 (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_fmt_ee (code, CCmode, op0, op1)));
return cc_reg;
}
int
mcore_symbolic_address_p (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 (rtx x, enum machine_mode mode)
{
return register_operand (x, mode) || CONSTANT_P (x);
}
char *
mcore_output_call (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 (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 (long value)
{
int x, y;
return try_constant_tricks (value, & x, & y) > 0;
}
int
mcore_const_trick_uses_not (long value)
{
int x, y;
return try_constant_tricks (value, & x, & y) == 2;
}
static int
try_constant_tricks (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 (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 (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 (int mask)
{
return 32 - mcore_num_ones (mask);
}
int
mcore_byte_offset (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 (unsigned int mask)
{
if (mask == 0x0000ffffL)
return 0;
else if (mask == 0xffff0000L)
return 1;
return -1;
}
const char *
mcore_output_bseti (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 (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 (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 (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 (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 (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
switch (GET_MODE (src))
{
case SImode:
return "ldw\t%0,%1";
case HImode:
return "ld.h\t%0,%1";
case QImode:
return "ld.b\t%0,%1";
default:
abort ();
}
}
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)
switch (GET_MODE (dst))
{
case SImode:
return "stw\t%1,%0";
case HImode:
return "st.h\t%1,%0";
case QImode:
return "st.b\t%1,%0";
default:
abort ();
}
abort ();
}
const char *
mcore_output_movedouble (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 (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 (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 (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 (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 (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 (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 (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 (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 (rtx op)
{
if (GET_CODE (op) == CONST_INT && CONST_OK_FOR_M (~INTVAL (op)))
return 1;
return 0;
}
int
mcore_arith_M_operand (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 (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 (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 (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 (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 (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 (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 (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 (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 (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,
};
static void
block_move_sequence (rtx dst_mem, rtx src_mem, int size, int align)
{
rtx temp[2];
enum machine_mode mode[2];
int amount[2];
bool active[2];
int phase = 0;
int next;
int offset_ld = 0;
int offset_st = 0;
rtx x;
x = XEXP (dst_mem, 0);
if (!REG_P (x))
{
x = force_reg (Pmode, x);
dst_mem = replace_equiv_address (dst_mem, x);
}
x = XEXP (src_mem, 0);
if (!REG_P (x))
{
x = force_reg (Pmode, x);
src_mem = replace_equiv_address (src_mem, x);
}
active[0] = active[1] = false;
do
{
next = phase;
phase ^= 1;
if (size > 0)
{
int next_amount;
next_amount = (size >= 4 ? 4 : (size >= 2 ? 2 : 1));
next_amount = MIN (next_amount, align);
amount[next] = next_amount;
mode[next] = mode_from_align[next_amount];
temp[next] = gen_reg_rtx (mode[next]);
x = adjust_address (src_mem, mode[next], offset_ld);
emit_insn (gen_rtx_SET (VOIDmode, temp[next], x));
offset_ld += next_amount;
size -= next_amount;
active[next] = true;
}
if (active[phase])
{
active[phase] = false;
x = adjust_address (dst_mem, mode[phase], offset_st);
emit_insn (gen_rtx_SET (VOIDmode, x, temp[phase]));
offset_st += amount[phase];
}
}
while (active[next]);
}
bool
mcore_expand_block_move (rtx *operands)
{
HOST_WIDE_INT align, bytes, max;
if (GET_CODE (operands[2]) != CONST_INT)
return false;
bytes = INTVAL (operands[2]);
align = INTVAL (operands[3]);
if (bytes <= 0)
return false;
if (align > 4)
align = 4;
switch (align)
{
case 4:
if (bytes & 1)
max = 4*4;
else if (bytes & 3)
max = 8*4;
else
max = 16*4;
break;
case 2:
max = 4*2;
break;
case 1:
max = 4*1;
break;
default:
abort ();
}
if (bytes <= max)
{
block_move_sequence (operands[0], operands[1], bytes, align);
return true;
}
return false;
}
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 (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 (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;
}
static void
mcore_setup_incoming_varargs (CUMULATIVE_ARGS *args_so_far,
enum machine_mode mode, tree type,
int * ptr_pretend_size ATTRIBUTE_UNUSED,
int second_time 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 (void)
{
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;
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);
mcore_current_function_name = xstrdup (XSTR (x, 0));
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 (void)
{
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 (void)
{
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;
(*targetm.asm_out.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 (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 (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 (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 (void)
{
rtx insn;
for (insn = get_insns (); insn; insn = conditionalize_block (insn))
continue;
}
static int saved_warn_return_type = -1;
static int saved_warn_return_type_count = 0;
static void
mcore_reorg (void)
{
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 ();
}
bool
mcore_r15_operand_p (rtx x)
{
switch (GET_CODE (x))
{
case CONST_INT:
return mcore_const_ok_for_inline (INTVAL (x));
case REG:
case SUBREG:
case MEM:
return 1;
default:
return 0;
}
}
enum reg_class
mcore_secondary_reload_class (enum reg_class class,
enum machine_mode mode ATTRIBUTE_UNUSED, rtx x)
{
if (TEST_HARD_REG_BIT (reg_class_contents[class], 15)
&& !mcore_r15_operand_p (x))
return LRW_REGS;
return NO_REGS;
}
enum reg_class
mcore_reload_class (rtx x, enum reg_class class)
{
if (reg_class_subset_p (LRW_REGS, class) && !mcore_r15_operand_p (x))
return LRW_REGS;
return class;
}
int
mcore_is_same_reg (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;
}
void
mcore_override_options (void)
{
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;
}
int
mcore_num_arg_regs (enum machine_mode mode, tree type)
{
int size;
if (targetm.calls.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 (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 (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 (CUMULATIVE_ARGS cum, enum machine_mode mode,
tree type, int named)
{
int arg_reg;
if (! named || mode == VOIDmode)
return 0;
if (targetm.calls.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;
}
static int
mcore_arg_partial_bytes (CUMULATIVE_ARGS *cum, enum machine_mode mode,
tree type, bool named)
{
int reg = ROUND_REG (*cum, mode);
if (named == 0)
return 0;
if (targetm.calls.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 * UNITS_PER_WORD;
}
int
mcore_dllexport_name_p (const char * symbol)
{
return symbol[0] == '@' && symbol[1] == 'e' && symbol[2] == '.';
}
int
mcore_dllimport_name_p (const char * symbol)
{
return symbol[0] == '@' && symbol[1] == 'i' && symbol[2] == '.';
}
static void
mcore_mark_dllexport (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 (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 ("%Jinitialized variable '%D' is marked dllimport", decl, decl);
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 (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 (tree decl)
{
if ( TREE_CODE (decl) != VAR_DECL
&& TREE_CODE (decl) != FUNCTION_DECL)
return 0;
return lookup_attribute ("dllimport", DECL_ATTRIBUTES (decl)) != 0;
}
static void
mcore_encode_section_info (tree decl, rtx rtl ATTRIBUTE_UNUSED, int first ATTRIBUTE_UNUSED)
{
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;
}
}
static const char *
mcore_strip_name_encoding (const char * str)
{
return str + (str[0] == '@' ? 3 : 0);
}
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 (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 ("%qs attribute only applies to functions",
IDENTIFIER_POINTER (name));
*no_add_attrs = true;
}
return NULL_TREE;
}
static void
mcore_unique_section (tree decl, int reloc ATTRIBUTE_UNUSED)
{
int len;
const char * name;
char * string;
const char * prefix;
name = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl));
name = (* targetm.strip_name_encoding) (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 (void)
{
return lookup_attribute ("naked", DECL_ATTRIBUTES (current_function_decl)) != NULL_TREE;
}
#ifdef OBJECT_FORMAT_ELF
static void
mcore_asm_named_section (const char *name,
unsigned int flags ATTRIBUTE_UNUSED,
tree decl ATTRIBUTE_UNUSED)
{
fprintf (asm_out_file, "\t.section %s\n", name);
}
#endif
static void
mcore_external_libcall (rtx fun)
{
fprintf (asm_out_file, "\t.import\t");
assemble_name (asm_out_file, XSTR (fun, 0));
fprintf (asm_out_file, "\n");
}
static bool
mcore_return_in_memory (tree type, tree fntype ATTRIBUTE_UNUSED)
{
HOST_WIDE_INT size = int_size_in_bytes (type);
return (size == -1 || size > 2 * UNITS_PER_WORD);
}