#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "tm.h"
#include <signal.h>
#include "rtl.h"
#include "regs.h"
#include "hard-reg-set.h"
#include "real.h"
#include "insn-config.h"
#include "conditions.h"
#include "insn-attr.h"
#include "recog.h"
#include "toplev.h"
#include "output.h"
#include "tree.h"
#include "function.h"
#include "expr.h"
#include "optabs.h"
#include "flags.h"
#include "reload.h"
#include "tm_p.h"
#include "ggc.h"
#include "gstab.h"
#include "hashtab.h"
#include "debug.h"
#include "target.h"
#include "target-def.h"
#include "integrate.h"
#include "langhooks.h"
#include "cfglayout.h"
#include "score-mdaux.h"
#define GR_REG_CLASS_P(C) ((C) == G16_REGS || (C) == G32_REGS)
#define SP_REG_CLASS_P(C) \
((C) == CN_REG || (C) == LC_REG || (C) == SC_REG || (C) == SP_REGS)
#define CP_REG_CLASS_P(C) \
((C) == CP1_REGS || (C) == CP2_REGS || (C) == CP3_REGS || (C) == CPA_REGS)
#define CE_REG_CLASS_P(C) \
((C) == HI_REG || (C) == LO_REG || (C) == CE_REGS)
static int score_arg_partial_bytes (const CUMULATIVE_ARGS *,
enum machine_mode, tree, int);
static int score_symbol_insns (enum score_symbol_type);
static int score_address_insns (rtx, enum machine_mode);
static bool score_rtx_costs (rtx, enum rtx_code, enum rtx_code, int *);
static int score_address_cost (rtx);
#undef TARGET_ASM_FILE_START
#define TARGET_ASM_FILE_START th_asm_file_start
#undef TARGET_ASM_FILE_END
#define TARGET_ASM_FILE_END th_asm_file_end
#undef TARGET_ASM_FUNCTION_PROLOGUE
#define TARGET_ASM_FUNCTION_PROLOGUE th_function_prologue
#undef TARGET_ASM_FUNCTION_EPILOGUE
#define TARGET_ASM_FUNCTION_EPILOGUE th_function_epilogue
#undef TARGET_SCHED_ISSUE_RATE
#define TARGET_SCHED_ISSUE_RATE th_issue_rate
#undef TARGET_ASM_SELECT_RTX_SECTION
#define TARGET_ASM_SELECT_RTX_SECTION th_select_rtx_section
#undef TARGET_IN_SMALL_DATA_P
#define TARGET_IN_SMALL_DATA_P th_in_small_data_p
#undef TARGET_FUNCTION_OK_FOR_SIBCALL
#define TARGET_FUNCTION_OK_FOR_SIBCALL th_function_ok_for_sibcall
#undef TARGET_STRICT_ARGUMENT_NAMING
#define TARGET_STRICT_ARGUMENT_NAMING th_strict_argument_naming
#undef TARGET_ASM_OUTPUT_MI_THUNK
#define TARGET_ASM_OUTPUT_MI_THUNK th_output_mi_thunk
#undef TARGET_ASM_CAN_OUTPUT_MI_THUNK
#define TARGET_ASM_CAN_OUTPUT_MI_THUNK hook_bool_tree_hwi_hwi_tree_true
#undef TARGET_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_MUST_PASS_IN_STACK
#define TARGET_MUST_PASS_IN_STACK must_pass_in_stack_var_size
#undef TARGET_ARG_PARTIAL_BYTES
#define TARGET_ARG_PARTIAL_BYTES score_arg_partial_bytes
#undef TARGET_PASS_BY_REFERENCE
#define TARGET_PASS_BY_REFERENCE score_pass_by_reference
#undef TARGET_RETURN_IN_MEMORY
#define TARGET_RETURN_IN_MEMORY score_return_in_memory
#undef TARGET_RTX_COSTS
#define TARGET_RTX_COSTS score_rtx_costs
#undef TARGET_ADDRESS_COST
#define TARGET_ADDRESS_COST score_address_cost
#undef TARGET_DEFAULT_TARGET_FLAGS
#define TARGET_DEFAULT_TARGET_FLAGS TARGET_DEFAULT
static bool
score_return_in_memory (tree type, tree fndecl ATTRIBUTE_UNUSED)
{
return ((TYPE_MODE (type) == BLKmode)
|| (int_size_in_bytes (type) > 2 * UNITS_PER_WORD)
|| (int_size_in_bytes (type) == -1));
}
static bool
score_pass_by_reference (CUMULATIVE_ARGS *cum ATTRIBUTE_UNUSED,
enum machine_mode mode, tree type,
bool named ATTRIBUTE_UNUSED)
{
return targetm.calls.must_pass_in_stack (mode, type);
}
static rtx
score_add_offset (rtx temp, rtx reg, HOST_WIDE_INT offset)
{
if (!IMM_IN_RANGE (offset, 15, 1))
{
reg = expand_simple_binop (GET_MODE (reg), PLUS,
gen_int_mode (offset & 0xffffc000,
GET_MODE (reg)),
reg, NULL, 0, OPTAB_WIDEN);
offset &= 0x3fff;
}
return plus_constant (reg, offset);
}
static void
th_output_mi_thunk (FILE *file, tree thunk_fndecl ATTRIBUTE_UNUSED,
HOST_WIDE_INT delta, HOST_WIDE_INT vcall_offset,
tree function)
{
rtx this, temp1, temp2, insn, fnaddr;
no_new_pseudos = 1;
reload_completed = 1;
reset_block_changes ();
temp1 = gen_rtx_REG (Pmode, 8);
temp2 = gen_rtx_REG (Pmode, 9);
if (aggregate_value_p (TREE_TYPE (TREE_TYPE (function)), function))
this = gen_rtx_REG (Pmode, ARG_REG_FIRST + 1);
else
this = gen_rtx_REG (Pmode, ARG_REG_FIRST);
if (delta != 0)
{
rtx offset = GEN_INT (delta);
if (!CONST_OK_FOR_LETTER_P (delta, 'L'))
{
emit_move_insn (temp1, offset);
offset = temp1;
}
emit_insn (gen_add3_insn (this, this, offset));
}
if (vcall_offset != 0)
{
rtx addr;
emit_move_insn (temp1, gen_rtx_MEM (Pmode, this));
addr = score_add_offset (temp2, temp1, vcall_offset);
emit_move_insn (temp1, gen_rtx_MEM (Pmode, addr));
emit_insn (gen_add3_insn (this, this, temp1));
}
fnaddr = XEXP (DECL_RTL (function), 0);
insn = emit_call_insn (gen_sibcall_internal (fnaddr, const0_rtx));
SIBLING_CALL_P (insn) = 1;
insn = get_insns ();
insn_locators_initialize ();
split_all_insns_noflow ();
shorten_branches (insn);
final_start_function (insn, file, 1);
final (insn, file, 1);
final_end_function ();
reload_completed = 0;
no_new_pseudos = 0;
}
static bool
th_strict_argument_naming (CUMULATIVE_ARGS *ca ATTRIBUTE_UNUSED)
{
return true;
}
static bool
th_function_ok_for_sibcall (ATTRIBUTE_UNUSED tree decl,
ATTRIBUTE_UNUSED tree exp)
{
return true;
}
struct score_arg_info
{
unsigned int num_bytes;
unsigned int reg_words;
unsigned int reg_offset;
unsigned int stack_words;
unsigned int stack_offset;
};
static void
classify_arg (const CUMULATIVE_ARGS *cum, enum machine_mode mode,
tree type, int named, struct score_arg_info *info)
{
int even_reg_p;
unsigned int num_words, max_regs;
even_reg_p = 0;
if (GET_MODE_CLASS (mode) == MODE_INT
|| GET_MODE_CLASS (mode) == MODE_FLOAT)
even_reg_p = (GET_MODE_SIZE (mode) > UNITS_PER_WORD);
else
if (type != NULL_TREE && TYPE_ALIGN (type) > BITS_PER_WORD && named)
even_reg_p = 1;
if (TARGET_MUST_PASS_IN_STACK (mode, type))
info->reg_offset = ARG_REG_NUM;
else
{
info->reg_offset = cum->num_gprs;
if (even_reg_p)
info->reg_offset += info->reg_offset & 1;
}
if (mode == BLKmode)
info->num_bytes = int_size_in_bytes (type);
else
info->num_bytes = GET_MODE_SIZE (mode);
num_words = (info->num_bytes + UNITS_PER_WORD - 1) / UNITS_PER_WORD;
max_regs = ARG_REG_NUM - info->reg_offset;
info->reg_words = MIN (num_words, max_regs);
info->stack_words = num_words - info->reg_words;
if (info->stack_words)
{
info->stack_offset = cum->stack_words;
if (even_reg_p)
info->stack_offset += info->stack_offset & 1;
}
}
static void
th_function_prologue (FILE *file, HOST_WIDE_INT size ATTRIBUTE_UNUSED)
{
const char *fnname;
struct score_frame_info *f = mda_cached_frame ();
HOST_WIDE_INT tsize = f->total_size;
fnname = XSTR (XEXP (DECL_RTL (current_function_decl), 0), 0);
if (!flag_inhibit_size_directive)
{
fputs ("\t.ent\t", file);
assemble_name (file, fnname);
fputs ("\n", file);
}
assemble_name (file, fnname);
fputs (":\n", file);
if (!flag_inhibit_size_directive)
{
fprintf (file,
"\t.frame\t%s," HOST_WIDE_INT_PRINT_DEC ",%s, %d\t\t"
"# vars= " HOST_WIDE_INT_PRINT_DEC ", regs= %d"
", args= " HOST_WIDE_INT_PRINT_DEC
", gp= " HOST_WIDE_INT_PRINT_DEC "\n",
(reg_names[(frame_pointer_needed)
? HARD_FRAME_POINTER_REGNUM : STACK_POINTER_REGNUM]),
tsize,
reg_names[RA_REGNUM],
current_function_is_leaf ? 1 : 0,
f->var_size,
f->num_gp,
f->args_size,
f->cprestore_size);
fprintf(file, "\t.mask\t0x%08x," HOST_WIDE_INT_PRINT_DEC "\n",
f->mask,
(f->gp_sp_offset - f->total_size));
}
}
static void
th_function_epilogue (FILE *file,
HOST_WIDE_INT size ATTRIBUTE_UNUSED)
{
if (!flag_inhibit_size_directive)
{
const char *fnname;
fnname = XSTR (XEXP (DECL_RTL (current_function_decl), 0), 0);
fputs ("\t.end\t", file);
assemble_name (file, fnname);
fputs ("\n", file);
}
}
static int
th_issue_rate (void)
{
return 1;
}
static bool
symbolic_expression_p (rtx x)
{
if (GET_CODE (x) == SYMBOL_REF)
return true;
if (GET_CODE (x) == CONST)
return symbolic_expression_p (XEXP (x, 0));
if (UNARY_P (x))
return symbolic_expression_p (XEXP (x, 0));
if (ARITHMETIC_P (x))
return (symbolic_expression_p (XEXP (x, 0))
|| symbolic_expression_p (XEXP (x, 1)));
return false;
}
static section *
th_select_rtx_section (enum machine_mode mode, rtx x,
unsigned HOST_WIDE_INT align)
{
if (GET_MODE_SIZE (mode) <= SCORE_SDATA_MAX)
return get_named_section (0, ".sdata", 0);
else if (flag_pic && symbolic_expression_p (x))
return get_named_section (0, ".data.rel.ro", 3);
else
return mergeable_constant_section (mode, align, 0);
}
static bool
th_in_small_data_p (tree decl)
{
HOST_WIDE_INT size;
if (TREE_CODE (decl) == STRING_CST
|| TREE_CODE (decl) == FUNCTION_DECL)
return false;
if (TREE_CODE (decl) == VAR_DECL && DECL_SECTION_NAME (decl) != 0)
{
const char *name;
name = TREE_STRING_POINTER (DECL_SECTION_NAME (decl));
if (strcmp (name, ".sdata") != 0
&& strcmp (name, ".sbss") != 0)
return true;
if (!DECL_EXTERNAL (decl))
return false;
}
size = int_size_in_bytes (TREE_TYPE (decl));
return (size > 0 && size <= SCORE_SDATA_MAX);
}
static void
th_asm_file_start (void)
{
default_file_start ();
fprintf (asm_out_file, ASM_COMMENT_START
"GCC for S+core %s \n", SCORE_GCC_VERSION);
if (flag_pic)
fprintf (asm_out_file, "\t.set pic\n");
}
struct extern_list *extern_head = 0;
static void
th_asm_file_end (void)
{
tree name_tree;
struct extern_list *p;
if (extern_head)
{
fputs ("\n", asm_out_file);
for (p = extern_head; p != 0; p = p->next)
{
name_tree = get_identifier (p->name);
if (!TREE_ASM_WRITTEN (name_tree)
&& TREE_SYMBOL_REFERENCED (name_tree))
{
TREE_ASM_WRITTEN (name_tree) = 1;
fputs ("\t.extern\t", asm_out_file);
assemble_name (asm_out_file, p->name);
fprintf (asm_out_file, ", %d\n", p->size);
}
}
}
}
static unsigned int sdata_max;
int
score_sdata_max (void)
{
return sdata_max;
}
enum reg_class score_char_to_class[256];
void
score_override_options (void)
{
flag_pic = false;
if (!flag_pic)
sdata_max = g_switch_set ? g_switch_value : DEFAULT_SDATA_MAX;
else
{
sdata_max = 0;
if (g_switch_set && (g_switch_value != 0))
warning (0, "-fPIC and -G are incompatible");
}
score_char_to_class['d'] = G32_REGS;
score_char_to_class['e'] = G16_REGS;
score_char_to_class['t'] = T32_REGS;
score_char_to_class['h'] = HI_REG;
score_char_to_class['l'] = LO_REG;
score_char_to_class['x'] = CE_REGS;
score_char_to_class['q'] = CN_REG;
score_char_to_class['y'] = LC_REG;
score_char_to_class['z'] = SC_REG;
score_char_to_class['a'] = SP_REGS;
score_char_to_class['c'] = CR_REGS;
score_char_to_class['b'] = CP1_REGS;
score_char_to_class['f'] = CP2_REGS;
score_char_to_class['i'] = CP3_REGS;
score_char_to_class['j'] = CPA_REGS;
}
int
score_reg_class (int regno)
{
int c;
gcc_assert (regno >= 0 && regno < FIRST_PSEUDO_REGISTER);
if (regno == FRAME_POINTER_REGNUM
|| regno == ARG_POINTER_REGNUM)
return ALL_REGS;
for (c = 0; c < N_REG_CLASSES; c++)
if (TEST_HARD_REG_BIT (reg_class_contents[c], regno))
return c;
return NO_REGS;
}
enum reg_class
score_preferred_reload_class (rtx x ATTRIBUTE_UNUSED, enum reg_class class)
{
if (reg_class_subset_p (G16_REGS, class))
return G16_REGS;
if (reg_class_subset_p (G32_REGS, class))
return G32_REGS;
return class;
}
enum reg_class
score_secondary_reload_class (enum reg_class class,
enum machine_mode mode ATTRIBUTE_UNUSED,
rtx x)
{
int regno = -1;
if (GET_CODE (x) == REG || GET_CODE(x) == SUBREG)
regno = true_regnum (x);
if (!GR_REG_CLASS_P (class))
return GP_REG_P (regno) ? NO_REGS : G32_REGS;
return NO_REGS;
}
int
score_const_ok_for_letter_p (HOST_WIDE_INT value, char c)
{
switch (c)
{
case 'I': return ((value & 0xffff) == 0);
case 'J': return IMM_IN_RANGE (value, 5, 0);
case 'K': return IMM_IN_RANGE (value, 16, 0);
case 'L': return IMM_IN_RANGE (value, 16, 1);
case 'M': return IMM_IN_RANGE (value, 14, 0);
case 'N': return IMM_IN_RANGE (value, 14, 1);
default : return 0;
}
}
int
score_extra_constraint (rtx op, char c)
{
switch (c)
{
case 'Z':
return GET_CODE (op) == SYMBOL_REF;
default:
gcc_unreachable ();
}
}
int
score_hard_regno_mode_ok (unsigned int regno, enum machine_mode mode)
{
int size = GET_MODE_SIZE (mode);
enum mode_class class = GET_MODE_CLASS (mode);
if (class == MODE_CC)
return regno == CC_REGNUM;
else if (regno == FRAME_POINTER_REGNUM
|| regno == ARG_POINTER_REGNUM)
return class == MODE_INT;
else if (GP_REG_P (regno))
return !(regno & 1) || (size <= UNITS_PER_WORD);
else if (CE_REG_P (regno))
return (class == MODE_INT
&& ((size <= UNITS_PER_WORD)
|| (regno == CE_REG_FIRST && size == 2 * UNITS_PER_WORD)));
else
return (class == MODE_INT) && (size <= UNITS_PER_WORD);
}
HOST_WIDE_INT
score_initial_elimination_offset (int from,
int to ATTRIBUTE_UNUSED)
{
struct score_frame_info *f = mda_compute_frame_size (get_frame_size ());
switch (from)
{
case ARG_POINTER_REGNUM:
return f->total_size;
case FRAME_POINTER_REGNUM:
return 0;
default:
gcc_unreachable ();
}
}
void
score_init_cumulative_args (CUMULATIVE_ARGS *cum,
tree fntype ATTRIBUTE_UNUSED,
rtx libname ATTRIBUTE_UNUSED)
{
memset (cum, 0, sizeof (CUMULATIVE_ARGS));
}
void
score_function_arg_advance (CUMULATIVE_ARGS *cum, enum machine_mode mode,
tree type, int named)
{
struct score_arg_info info;
classify_arg (cum, mode, type, named, &info);
cum->num_gprs = info.reg_offset + info.reg_words;
if (info.stack_words > 0)
cum->stack_words = info.stack_offset + info.stack_words;
cum->arg_number++;
}
static int
score_arg_partial_bytes (const CUMULATIVE_ARGS *cum,
enum machine_mode mode, tree type, int named)
{
struct score_arg_info info;
classify_arg (cum, mode, type, named, &info);
return info.stack_words > 0 ? info.reg_words * UNITS_PER_WORD : 0;
}
rtx
score_function_arg (const CUMULATIVE_ARGS *cum, enum machine_mode mode,
tree type, int named)
{
struct score_arg_info info;
if (mode == VOIDmode || !named)
return 0;
classify_arg (cum, mode, type, named, &info);
if (info.reg_offset == ARG_REG_NUM)
return 0;
if (!info.stack_words)
return gen_rtx_REG (mode, ARG_REG_FIRST + info.reg_offset);
else
{
rtx ret = gen_rtx_PARALLEL (mode, rtvec_alloc (info.reg_words));
unsigned int i, part_offset = 0;
for (i = 0; i < info.reg_words; i++)
{
rtx reg;
reg = gen_rtx_REG (SImode, ARG_REG_FIRST + info.reg_offset + i);
XVECEXP (ret, 0, i) = gen_rtx_EXPR_LIST (SImode, reg,
GEN_INT (part_offset));
part_offset += UNITS_PER_WORD;
}
return ret;
}
}
rtx
score_function_value (tree valtype, tree func ATTRIBUTE_UNUSED,
enum machine_mode mode)
{
if (valtype)
{
int unsignedp;
mode = TYPE_MODE (valtype);
unsignedp = TYPE_UNSIGNED (valtype);
mode = promote_mode (valtype, mode, &unsignedp, 1);
}
return gen_rtx_REG (mode, RT_REGNUM);
}
void
score_initialize_trampoline (rtx ADDR, rtx FUNC, rtx CHAIN)
{
#define FFCACHE "_flush_cache"
#define CODE_SIZE (TRAMPOLINE_INSNS * UNITS_PER_WORD)
unsigned int tramp[TRAMPOLINE_INSNS] = {
0x8103bc56,
0x9000bc05,
0xc1238000 | (CODE_SIZE - 8),
0xc0038000
| (STATIC_CHAIN_REGNUM << 21)
| (CODE_SIZE - 4),
0x8068bc56,
0x8009bc08,
0x0,
0x0,
};
rtx pfunc, pchain;
int i;
for (i = 0; i < TRAMPOLINE_INSNS; i++)
emit_move_insn (gen_rtx_MEM (ptr_mode, plus_constant (ADDR, i << 2)),
GEN_INT (tramp[i]));
pfunc = plus_constant (ADDR, CODE_SIZE);
pchain = plus_constant (ADDR, CODE_SIZE + GET_MODE_SIZE (ptr_mode));
emit_move_insn (gen_rtx_MEM (ptr_mode, pfunc), FUNC);
emit_move_insn (gen_rtx_MEM (ptr_mode, pchain), CHAIN);
emit_library_call (gen_rtx_SYMBOL_REF (Pmode, FFCACHE),
0, VOIDmode, 2,
ADDR, Pmode,
GEN_INT (TRAMPOLINE_SIZE), SImode);
#undef FFCACHE
#undef CODE_SIZE
}
int
score_regno_mode_ok_for_base_p (int regno, int strict)
{
if (regno >= FIRST_PSEUDO_REGISTER)
{
if (!strict)
return 1;
regno = reg_renumber[regno];
}
if (regno == ARG_POINTER_REGNUM
|| regno == FRAME_POINTER_REGNUM)
return 1;
return GP_REG_P (regno);
}
int
score_address_p (enum machine_mode mode, rtx x, int strict)
{
struct score_address_info addr;
return mda_classify_address (&addr, mode, x, strict);
}
static rtx
score_force_temporary (rtx dest, rtx value)
{
if (!no_new_pseudos)
return force_reg (Pmode, value);
else
{
emit_move_insn (copy_rtx (dest), value);
return dest;
}
}
static rtx
score_split_symbol (rtx temp, rtx addr)
{
rtx high = score_force_temporary (temp,
gen_rtx_HIGH (Pmode, copy_rtx (addr)));
return gen_rtx_LO_SUM (Pmode, high, addr);
}
int
score_legitimize_address (rtx *xloc)
{
enum score_symbol_type symbol_type;
if (mda_symbolic_constant_p (*xloc, &symbol_type)
&& symbol_type == SYMBOL_GENERAL)
{
*xloc = score_split_symbol (0, *xloc);
return 1;
}
if (GET_CODE (*xloc) == PLUS
&& GET_CODE (XEXP (*xloc, 1)) == CONST_INT)
{
rtx reg = XEXP (*xloc, 0);
if (!mda_valid_base_register_p (reg, 0))
reg = copy_to_mode_reg (Pmode, reg);
*xloc = score_add_offset (NULL, reg, INTVAL (XEXP (*xloc, 1)));
return 1;
}
return 0;
}
int
score_register_move_cost (enum machine_mode mode ATTRIBUTE_UNUSED,
enum reg_class from, enum reg_class to)
{
if (GR_REG_CLASS_P (from))
{
if (GR_REG_CLASS_P (to))
return 2;
else if (SP_REG_CLASS_P (to))
return 4;
else if (CP_REG_CLASS_P (to))
return 5;
else if (CE_REG_CLASS_P (to))
return 6;
}
if (GR_REG_CLASS_P (to))
{
if (GR_REG_CLASS_P (from))
return 2;
else if (SP_REG_CLASS_P (from))
return 4;
else if (CP_REG_CLASS_P (from))
return 5;
else if (CE_REG_CLASS_P (from))
return 6;
}
return 12;
}
static int
score_symbol_insns (enum score_symbol_type type)
{
switch (type)
{
case SYMBOL_GENERAL:
return 2;
case SYMBOL_SMALL_DATA:
return 1;
}
gcc_unreachable ();
}
static int
score_address_insns (rtx x, enum machine_mode mode)
{
struct score_address_info addr;
int factor;
if (mode == BLKmode)
factor = 1;
else
factor = (GET_MODE_SIZE (mode) + UNITS_PER_WORD - 1) / UNITS_PER_WORD;
if (mda_classify_address (&addr, mode, x, false))
switch (addr.type)
{
case ADD_REG:
case ADD_CONST_INT:
return factor;
case ADD_SYMBOLIC:
return factor * score_symbol_insns (addr.symbol_type);
}
return 0;
}
static bool
score_rtx_costs (rtx x, enum rtx_code code, enum rtx_code outer_code,
int *total)
{
enum machine_mode mode = GET_MODE (x);
switch (code)
{
case CONST_INT:
if (outer_code == SET)
{
if (CONST_OK_FOR_LETTER_P (INTVAL (x), 'I')
|| CONST_OK_FOR_LETTER_P (INTVAL (x), 'L'))
*total = COSTS_N_INSNS (1);
else
*total = COSTS_N_INSNS (2);
}
else if (outer_code == PLUS || outer_code == MINUS)
{
if (CONST_OK_FOR_LETTER_P (INTVAL (x), 'N'))
*total = 0;
else if (CONST_OK_FOR_LETTER_P (INTVAL (x), 'I')
|| CONST_OK_FOR_LETTER_P (INTVAL (x), 'L'))
*total = 1;
else
*total = COSTS_N_INSNS (2);
}
else if (outer_code == AND || outer_code == IOR)
{
if (CONST_OK_FOR_LETTER_P (INTVAL (x), 'M'))
*total = 0;
else if (CONST_OK_FOR_LETTER_P (INTVAL (x), 'I')
|| CONST_OK_FOR_LETTER_P (INTVAL (x), 'K'))
*total = 1;
else
*total = COSTS_N_INSNS (2);
}
else
{
*total = 0;
}
return true;
case CONST:
case SYMBOL_REF:
case LABEL_REF:
case CONST_DOUBLE:
*total = COSTS_N_INSNS (2);
return true;
case MEM:
{
int n = score_address_insns (XEXP (x, 0), GET_MODE (x));
if (n > 0)
{
*total = COSTS_N_INSNS (n + 1);
return true;
}
return false;
}
case FFS:
*total = COSTS_N_INSNS (6);
return true;
case NOT:
*total = COSTS_N_INSNS (1);
return true;
case AND:
case IOR:
case XOR:
if (mode == DImode)
{
*total = COSTS_N_INSNS (2);
return true;
}
return false;
case ASHIFT:
case ASHIFTRT:
case LSHIFTRT:
if (mode == DImode)
{
*total = COSTS_N_INSNS ((GET_CODE (XEXP (x, 1)) == CONST_INT)
? 4 : 12);
return true;
}
return false;
case ABS:
*total = COSTS_N_INSNS (4);
return true;
case PLUS:
case MINUS:
if (mode == DImode)
{
*total = COSTS_N_INSNS (4);
return true;
}
*total = COSTS_N_INSNS (1);
return true;
case NEG:
if (mode == DImode)
{
*total = COSTS_N_INSNS (4);
return true;
}
return false;
case MULT:
*total = optimize_size ? COSTS_N_INSNS (2) : COSTS_N_INSNS (12);
return true;
case DIV:
case MOD:
case UDIV:
case UMOD:
*total = optimize_size ? COSTS_N_INSNS (2) : COSTS_N_INSNS (33);
return true;
case SIGN_EXTEND:
case ZERO_EXTEND:
switch (GET_MODE (XEXP (x, 0)))
{
case QImode:
case HImode:
if (GET_CODE (XEXP (x, 0)) == MEM)
{
*total = COSTS_N_INSNS (2);
if (!TARGET_LITTLE_ENDIAN &&
side_effects_p (XEXP (XEXP (x, 0), 0)))
*total = 100;
}
else
*total = COSTS_N_INSNS (1);
break;
default:
*total = COSTS_N_INSNS (1);
break;
}
return true;
default:
return false;
}
}
int
score_address_cost (rtx addr)
{
return score_address_insns (addr, SImode);
}
int
score_output_external (FILE *file ATTRIBUTE_UNUSED,
tree decl, const char *name)
{
register struct extern_list *p;
if (th_in_small_data_p (decl))
{
p = (struct extern_list *) ggc_alloc (sizeof (struct extern_list));
p->next = extern_head;
p->name = name;
p->size = int_size_in_bytes (TREE_TYPE (decl));
extern_head = p;
}
return 0;
}
void
score_declare_object (FILE *stream, const char *name,
const char *directive, const char *fmt, ...)
{
va_list ap;
fputs (directive, stream);
assemble_name (stream, name);
va_start (ap, fmt);
vfprintf (stream, fmt, ap);
va_end (ap);
}
rtx
score_return_addr (int count, rtx frame ATTRIBUTE_UNUSED)
{
if (count != 0)
return const0_rtx;
return get_hard_reg_initial_val (Pmode, RA_REGNUM);
}
void
score_print_operand (FILE *file, rtx op, int c)
{
enum rtx_code code = -1;
if (!PRINT_OPERAND_PUNCT_VALID_P (c))
code = GET_CODE (op);
if (c == '[')
{
fprintf (file, ".set r1\n");
}
else if (c == ']')
{
fprintf (file, "\n\t.set nor1");
}
else if (c == 'U')
{
gcc_assert (code == CONST_INT);
fprintf (file, HOST_WIDE_INT_PRINT_HEX,
(INTVAL (op) >> 16) & 0xffff);
}
else if (c == 'D')
{
if (GET_CODE (op) == CONST_DOUBLE)
{
rtx temp = gen_lowpart (SImode, op);
gcc_assert (GET_MODE (op) == SFmode);
fprintf (file, HOST_WIDE_INT_PRINT_HEX, INTVAL (temp) & 0xffffffff);
}
else
output_addr_const (file, op);
}
else if (c == 'S')
{
gcc_assert (code == REG);
if (G16_REG_P (REGNO (op)))
fprintf (file, "!");
}
else if (c == 'V')
{
gcc_assert (code == REG);
fprintf (file, G16_REG_P (REGNO (op)) ? "v!" : "lfh!");
}
else if (c == 'C')
{
enum machine_mode mode = GET_MODE (XEXP (op, 0));
switch (code)
{
case EQ: fputs ("eq", file); break;
case NE: fputs ("ne", file); break;
case GT: fputs ("gt", file); break;
case GE: fputs (mode != CCmode ? "pl" : "ge", file); break;
case LT: fputs (mode != CCmode ? "mi" : "lt", file); break;
case LE: fputs ("le", file); break;
case GTU: fputs ("gtu", file); break;
case GEU: fputs ("cs", file); break;
case LTU: fputs ("cc", file); break;
case LEU: fputs ("leu", file); break;
default:
output_operand_lossage ("invalid operand for code: '%c'", code);
}
}
else if (c == 'E')
{
unsigned HOST_WIDE_INT i;
unsigned HOST_WIDE_INT pow2mask = 1;
unsigned HOST_WIDE_INT val;
val = INTVAL (op);
for (i = 0; i < 32; i++)
{
if (val == pow2mask)
break;
pow2mask <<= 1;
}
gcc_assert (i < 32);
fprintf (file, HOST_WIDE_INT_PRINT_HEX, i);
}
else if (c == 'F')
{
unsigned HOST_WIDE_INT i;
unsigned HOST_WIDE_INT pow2mask = 1;
unsigned HOST_WIDE_INT val;
val = ~INTVAL (op);
for (i = 0; i < 32; i++)
{
if (val == pow2mask)
break;
pow2mask <<= 1;
}
gcc_assert (i < 32);
fprintf (file, HOST_WIDE_INT_PRINT_HEX, i);
}
else if (code == REG)
{
int regnum = REGNO (op);
if ((c == 'H' && !WORDS_BIG_ENDIAN)
|| (c == 'L' && WORDS_BIG_ENDIAN))
regnum ++;
fprintf (file, "%s", reg_names[regnum]);
}
else
{
switch (code)
{
case MEM:
score_print_operand_address (file, op);
break;
default:
output_addr_const (file, op);
}
}
}
void
score_print_operand_address (FILE *file, rtx x)
{
struct score_address_info addr;
enum rtx_code code = GET_CODE (x);
enum machine_mode mode = GET_MODE (x);
if (code == MEM)
x = XEXP (x, 0);
if (mda_classify_address (&addr, mode, x, true))
{
switch (addr.type)
{
case ADD_REG:
{
switch (addr.code)
{
case PRE_DEC:
fprintf (file, "[%s,-%ld]+", reg_names[REGNO (addr.reg)],
INTVAL (addr.offset));
break;
case POST_DEC:
fprintf (file, "[%s]+,-%ld", reg_names[REGNO (addr.reg)],
INTVAL (addr.offset));
break;
case PRE_INC:
fprintf (file, "[%s, %ld]+", reg_names[REGNO (addr.reg)],
INTVAL (addr.offset));
break;
case POST_INC:
fprintf (file, "[%s]+, %ld", reg_names[REGNO (addr.reg)],
INTVAL (addr.offset));
break;
default:
fprintf (file, "[%s,%ld]", reg_names[REGNO (addr.reg)],
INTVAL (addr.offset));
break;
}
}
return;
case ADD_CONST_INT:
case ADD_SYMBOLIC:
output_addr_const (file, x);
return;
}
}
print_rtl (stderr, x);
gcc_unreachable ();
}
enum machine_mode
score_select_cc_mode (enum rtx_code op, rtx x, rtx y)
{
if ((op == EQ || op == NE || op == LT || op == GE)
&& y == const0_rtx
&& GET_MODE (x) == SImode)
{
switch (GET_CODE (x))
{
case PLUS:
case MINUS:
case NEG:
case AND:
case IOR:
case XOR:
case NOT:
case ASHIFT:
case LSHIFTRT:
case ASHIFTRT:
return CC_NZmode;
case SIGN_EXTEND:
case ZERO_EXTEND:
case ROTATE:
case ROTATERT:
return (op == LT || op == GE) ? CC_Nmode : CCmode;
default:
return CCmode;
}
}
if ((op == EQ || op == NE)
&& (GET_CODE (y) == NEG)
&& register_operand (XEXP (y, 0), SImode)
&& register_operand (x, SImode))
{
return CC_NZmode;
}
return CCmode;
}
struct gcc_target targetm = TARGET_INITIALIZER;