#include "config.h"
#include "system.h"
#include "coretypes.h"
#include <signal.h>
#include "tm.h"
#include "tree.h"
#include "rtl.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 "function.h"
#include "expr.h"
#include "optabs.h"
#include "libfuncs.h"
#include "recog.h"
#include "toplev.h"
#include "reload.h"
#include "ggc.h"
#include "tm_p.h"
#include "debug.h"
#include "target.h"
#include "target-def.h"
#include "langhooks.h"
enum internal_test
{
ITEST_EQ,
ITEST_NE,
ITEST_GT,
ITEST_GE,
ITEST_LT,
ITEST_LE,
ITEST_GTU,
ITEST_GEU,
ITEST_LTU,
ITEST_LEU,
ITEST_MAX
};
struct constant;
struct iq2000_frame_info
{
long total_size;
long var_size;
long args_size;
long extra_size;
int gp_reg_size;
int fp_reg_size;
long mask;
long gp_save_offset;
long fp_save_offset;
long gp_sp_offset;
long fp_sp_offset;
int initialized;
int num_gp;
} iq2000_frame_info;
struct machine_function GTY(())
{
long total_size;
long var_size;
long args_size;
long extra_size;
int gp_reg_size;
int fp_reg_size;
long mask;
long gp_save_offset;
long fp_save_offset;
long gp_sp_offset;
long fp_sp_offset;
int initialized;
int num_gp;
};
char iq2000_print_operand_punct[256];
enum processor_type iq2000_tune;
int iq2000_isa;
rtx branch_cmp[2];
enum cmp_type branch_type;
const char * iq2000_cpu_string;
const char * iq2000_arch_string;
static int iq2000_branch_likely;
static int dslots_load_total;
static int dslots_load_filled;
static int dslots_jump_total;
static int dslots_number_nops;
static int num_refs[3];
static rtx iq2000_load_reg;
static rtx iq2000_load_reg2;
static rtx iq2000_load_reg3;
static rtx iq2000_load_reg4;
static enum processor_type iq2000_arch;
static enum machine_mode gpr_mode;
static struct machine_function* iq2000_init_machine_status (void);
static void iq2000_select_rtx_section (enum machine_mode, rtx, unsigned HOST_WIDE_INT);
static void iq2000_init_builtins (void);
static rtx iq2000_expand_builtin (tree, rtx, rtx, enum machine_mode, int);
static bool iq2000_return_in_memory (tree, tree);
static void iq2000_setup_incoming_varargs (CUMULATIVE_ARGS *,
enum machine_mode, tree, int *,
int);
static bool iq2000_rtx_costs (rtx, int, int, int *);
static int iq2000_address_cost (rtx);
static void iq2000_select_section (tree, int, unsigned HOST_WIDE_INT);
static bool iq2000_return_in_memory (tree, tree);
static bool iq2000_pass_by_reference (CUMULATIVE_ARGS *, enum machine_mode,
tree, bool);
static int iq2000_arg_partial_bytes (CUMULATIVE_ARGS *, enum machine_mode,
tree, bool);
#undef TARGET_INIT_BUILTINS
#define TARGET_INIT_BUILTINS iq2000_init_builtins
#undef TARGET_EXPAND_BUILTIN
#define TARGET_EXPAND_BUILTIN iq2000_expand_builtin
#undef TARGET_ASM_SELECT_RTX_SECTION
#define TARGET_ASM_SELECT_RTX_SECTION iq2000_select_rtx_section
#undef TARGET_RTX_COSTS
#define TARGET_RTX_COSTS iq2000_rtx_costs
#undef TARGET_ADDRESS_COST
#define TARGET_ADDRESS_COST iq2000_address_cost
#undef TARGET_ASM_SELECT_SECTION
#define TARGET_ASM_SELECT_SECTION iq2000_select_section
#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 iq2000_return_in_memory
#undef TARGET_PASS_BY_REFERENCE
#define TARGET_PASS_BY_REFERENCE iq2000_pass_by_reference
#undef TARGET_CALLEE_COPIES
#define TARGET_CALLEE_COPIES hook_callee_copies_named
#undef TARGET_ARG_PARTIAL_BYTES
#define TARGET_ARG_PARTIAL_BYTES iq2000_arg_partial_bytes
#undef TARGET_SETUP_INCOMING_VARARGS
#define TARGET_SETUP_INCOMING_VARARGS iq2000_setup_incoming_varargs
#undef TARGET_STRICT_ARGUMENT_NAMING
#define TARGET_STRICT_ARGUMENT_NAMING hook_bool_CUMULATIVE_ARGS_true
struct gcc_target targetm = TARGET_INITIALIZER;
int
uns_arith_operand (rtx op, enum machine_mode mode)
{
if (GET_CODE (op) == CONST_INT && SMALL_INT_UNSIGNED (op))
return 1;
return register_operand (op, mode);
}
int
arith_operand (rtx op, enum machine_mode mode)
{
if (GET_CODE (op) == CONST_INT && SMALL_INT (op))
return 1;
return register_operand (op, mode);
}
int
small_int (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
{
return (GET_CODE (op) == CONST_INT && SMALL_INT (op));
}
int
large_int (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
{
HOST_WIDE_INT value;
if (GET_CODE (op) != CONST_INT)
return 0;
value = INTVAL (op);
if ((value & ~ ((HOST_WIDE_INT) 0x0000ffff)) == 0)
return 0;
if (((unsigned HOST_WIDE_INT) (value + 32768)) <= 32767)
return 0;
if ((value & 0x0000ffff) == 0)
return 0;
return 1;
}
int
reg_or_0_operand (rtx op, enum machine_mode mode)
{
switch (GET_CODE (op))
{
case CONST_INT:
return INTVAL (op) == 0;
case CONST_DOUBLE:
return op == CONST0_RTX (mode);
case REG:
case SUBREG:
return register_operand (op, mode);
default:
break;
}
return 0;
}
int
simple_memory_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
{
rtx addr, plus0, plus1;
if (GET_CODE (op) != MEM)
return 0;
if (GET_MODE_SIZE (GET_MODE (op)) > (unsigned) UNITS_PER_WORD)
return 0;
addr = XEXP (op, 0);
switch (GET_CODE (addr))
{
case REG:
case LO_SUM:
return 1;
case CONST_INT:
return SMALL_INT (addr);
case PLUS:
plus0 = XEXP (addr, 0);
plus1 = XEXP (addr, 1);
if (GET_CODE (plus0) == REG
&& GET_CODE (plus1) == CONST_INT && SMALL_INT (plus1)
&& SMALL_INT_UNSIGNED (plus1) )
return 1;
else if (GET_CODE (plus1) == REG
&& GET_CODE (plus0) == CONST_INT && SMALL_INT (plus0)
&& SMALL_INT_UNSIGNED (plus1) )
return 1;
else
return 0;
case SYMBOL_REF:
return 0;
default:
break;
}
return 0;
}
int
equality_op (rtx op, enum machine_mode mode)
{
if (mode != GET_MODE (op))
return 0;
return GET_CODE (op) == EQ || GET_CODE (op) == NE;
}
int
cmp_op (rtx op, enum machine_mode mode)
{
if (mode != GET_MODE (op))
return 0;
return COMPARISON_P (op);
}
int
pc_or_label_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
{
if (op == pc_rtx)
return 1;
if (GET_CODE (op) == LABEL_REF)
return 1;
return 0;
}
int
call_insn_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
{
return (CONSTANT_ADDRESS_P (op)
|| (GET_CODE (op) == REG && op != arg_pointer_rtx
&& ! (REGNO (op) >= FIRST_PSEUDO_REGISTER
&& REGNO (op) <= LAST_VIRTUAL_REGISTER)));
}
int
move_operand (rtx op, enum machine_mode mode)
{
return (general_operand (op, mode)
&& (! (iq2000_check_split (op, mode))
|| reload_in_progress || reload_completed));
}
int
power_of_2_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
{
int intval;
if (GET_CODE (op) != CONST_INT)
return 0;
else
intval = INTVAL (op);
return ((intval & ((unsigned)(intval) - 1)) == 0);
}
int
iq2000_check_split (rtx address, enum machine_mode mode)
{
if (GET_MODE_SIZE (mode) > (unsigned) UNITS_PER_WORD)
return 0;
if ((GET_CODE (address) == SYMBOL_REF)
|| (GET_CODE (address) == CONST
&& GET_CODE (XEXP (XEXP (address, 0), 0)) == SYMBOL_REF)
|| GET_CODE (address) == LABEL_REF)
return 1;
return 0;
}
int
iq2000_reg_mode_ok_for_base_p (rtx reg,
enum machine_mode mode ATTRIBUTE_UNUSED,
int strict)
{
return (strict
? REGNO_MODE_OK_FOR_BASE_P (REGNO (reg), mode)
: GP_REG_OR_PSEUDO_NONSTRICT_P (REGNO (reg), mode));
}
int
iq2000_legitimate_address_p (enum machine_mode mode, rtx xinsn, int strict)
{
if (TARGET_DEBUG_A_MODE)
{
GO_PRINTF2 ("\n========== GO_IF_LEGITIMATE_ADDRESS, %sstrict\n",
strict ? "" : "not ");
GO_DEBUG_RTX (xinsn);
}
if (CONSTANT_ADDRESS_P (xinsn)
&& ! (iq2000_check_split (xinsn, mode))
&& ! (GET_CODE (xinsn) == CONST_INT && ! SMALL_INT (xinsn)))
return 1;
while (GET_CODE (xinsn) == SUBREG)
xinsn = SUBREG_REG (xinsn);
if (GET_CODE (xinsn) == REG
&& iq2000_reg_mode_ok_for_base_p (xinsn, mode, strict))
return 1;
if (GET_CODE (xinsn) == LO_SUM)
{
rtx xlow0 = XEXP (xinsn, 0);
rtx xlow1 = XEXP (xinsn, 1);
while (GET_CODE (xlow0) == SUBREG)
xlow0 = SUBREG_REG (xlow0);
if (GET_CODE (xlow0) == REG
&& iq2000_reg_mode_ok_for_base_p (xlow0, mode, strict)
&& iq2000_check_split (xlow1, mode))
return 1;
}
if (GET_CODE (xinsn) == PLUS)
{
rtx xplus0 = XEXP (xinsn, 0);
rtx xplus1 = XEXP (xinsn, 1);
enum rtx_code code0;
enum rtx_code code1;
while (GET_CODE (xplus0) == SUBREG)
xplus0 = SUBREG_REG (xplus0);
code0 = GET_CODE (xplus0);
while (GET_CODE (xplus1) == SUBREG)
xplus1 = SUBREG_REG (xplus1);
code1 = GET_CODE (xplus1);
if (code0 == REG
&& iq2000_reg_mode_ok_for_base_p (xplus0, mode, strict))
{
if (code1 == CONST_INT && SMALL_INT (xplus1)
&& SMALL_INT_UNSIGNED (xplus1) )
return 1;
}
}
if (TARGET_DEBUG_A_MODE)
GO_PRINTF ("Not a legitimate address\n");
return 0;
}
const char *
iq2000_fill_delay_slot (const char *ret, enum delay_type type, rtx operands[],
rtx cur_insn)
{
rtx set_reg;
enum machine_mode mode;
rtx next_insn = cur_insn ? NEXT_INSN (cur_insn) : NULL_RTX;
int num_nops;
if (type == DELAY_LOAD || type == DELAY_FCMP)
num_nops = 1;
else
num_nops = 0;
next_insn = NEXT_INSN (cur_insn);
while (next_insn != 0
&& (GET_CODE (next_insn) == NOTE
|| GET_CODE (next_insn) == CODE_LABEL))
next_insn = NEXT_INSN (next_insn);
dslots_load_total += num_nops;
if (TARGET_DEBUG_C_MODE
|| type == DELAY_NONE
|| operands == 0
|| cur_insn == 0
|| next_insn == 0
|| GET_CODE (next_insn) == CODE_LABEL
|| (set_reg = operands[0]) == 0)
{
dslots_number_nops = 0;
iq2000_load_reg = 0;
iq2000_load_reg2 = 0;
iq2000_load_reg3 = 0;
iq2000_load_reg4 = 0;
return ret;
}
set_reg = operands[0];
if (set_reg == 0)
return ret;
while (GET_CODE (set_reg) == SUBREG)
set_reg = SUBREG_REG (set_reg);
mode = GET_MODE (set_reg);
dslots_number_nops = num_nops;
iq2000_load_reg = set_reg;
if (GET_MODE_SIZE (mode)
> (unsigned) (UNITS_PER_WORD))
iq2000_load_reg2 = gen_rtx_REG (SImode, REGNO (set_reg) + 1);
else
iq2000_load_reg2 = 0;
return ret;
}
static void
iq2000_count_memory_refs (rtx op, int num)
{
int additional = 0;
int n_words = 0;
rtx addr, plus0, plus1;
enum rtx_code code0, code1;
int looping;
if (TARGET_DEBUG_B_MODE)
{
fprintf (stderr, "\n========== iq2000_count_memory_refs:\n");
debug_rtx (op);
}
addr = (GET_CODE (op) != MEM) ? op : XEXP (op, 0);
do
{
looping = FALSE;
switch (GET_CODE (addr))
{
case REG:
case CONST_INT:
case LO_SUM:
break;
case PLUS:
plus0 = XEXP (addr, 0);
plus1 = XEXP (addr, 1);
code0 = GET_CODE (plus0);
code1 = GET_CODE (plus1);
if (code0 == REG)
{
additional++;
addr = plus1;
looping = 1;
continue;
}
if (code0 == CONST_INT)
{
addr = plus1;
looping = 1;
continue;
}
if (code1 == REG)
{
additional++;
addr = plus0;
looping = 1;
continue;
}
if (code1 == CONST_INT)
{
addr = plus0;
looping = 1;
continue;
}
if (code0 == SYMBOL_REF || code0 == LABEL_REF || code0 == CONST)
{
addr = plus0;
looping = 1;
continue;
}
if (code1 == SYMBOL_REF || code1 == LABEL_REF || code1 == CONST)
{
addr = plus1;
looping = 1;
continue;
}
break;
case LABEL_REF:
n_words = 2;
break;
case CONST:
addr = XEXP (addr, 0);
looping = 1;
continue;
case SYMBOL_REF:
n_words = SYMBOL_REF_FLAG (addr) ? 1 : 2;
break;
default:
break;
}
}
while (looping);
if (n_words == 0)
return;
n_words += additional;
if (n_words > 3)
n_words = 3;
num_refs[n_words-1] += num;
}
static void
abort_with_insn (rtx insn, const char * reason)
{
error (reason);
debug_rtx (insn);
abort ();
}
const char *
iq2000_move_1word (rtx operands[], rtx insn, int unsignedp)
{
const char *ret = 0;
rtx op0 = operands[0];
rtx op1 = operands[1];
enum rtx_code code0 = GET_CODE (op0);
enum rtx_code code1 = GET_CODE (op1);
enum machine_mode mode = GET_MODE (op0);
int subreg_offset0 = 0;
int subreg_offset1 = 0;
enum delay_type delay = DELAY_NONE;
while (code0 == SUBREG)
{
subreg_offset0 += subreg_regno_offset (REGNO (SUBREG_REG (op0)),
GET_MODE (SUBREG_REG (op0)),
SUBREG_BYTE (op0),
GET_MODE (op0));
op0 = SUBREG_REG (op0);
code0 = GET_CODE (op0);
}
while (code1 == SUBREG)
{
subreg_offset1 += subreg_regno_offset (REGNO (SUBREG_REG (op1)),
GET_MODE (SUBREG_REG (op1)),
SUBREG_BYTE (op1),
GET_MODE (op1));
op1 = SUBREG_REG (op1);
code1 = GET_CODE (op1);
}
if (mode == CCmode)
mode = SImode;
if (code0 == REG)
{
int regno0 = REGNO (op0) + subreg_offset0;
if (code1 == REG)
{
int regno1 = REGNO (op1) + subreg_offset1;
if (regno0 == regno1)
ret = "";
else if (GP_REG_P (regno0))
{
if (GP_REG_P (regno1))
ret = "or\t%0,%%0,%1";
}
}
else if (code1 == MEM)
{
delay = DELAY_LOAD;
if (TARGET_STATS)
iq2000_count_memory_refs (op1, 1);
if (GP_REG_P (regno0))
{
switch (GET_MODE (op1))
{
default:
break;
case SFmode:
ret = "lw\t%0,%1";
break;
case SImode:
case CCmode:
ret = "lw\t%0,%1";
break;
case HImode:
ret = (unsignedp) ? "lhu\t%0,%1" : "lh\t%0,%1";
break;
case QImode:
ret = (unsignedp) ? "lbu\t%0,%1" : "lb\t%0,%1";
break;
}
}
}
else if (code1 == CONST_INT
|| (code1 == CONST_DOUBLE
&& GET_MODE (op1) == VOIDmode))
{
if (code1 == CONST_DOUBLE)
{
operands[1] = op1 = GEN_INT (CONST_DOUBLE_LOW (op1));
}
if (INTVAL (op1) == 0)
{
if (GP_REG_P (regno0))
ret = "or\t%0,%%0,%z1";
}
else if (GP_REG_P (regno0))
{
if (SMALL_INT_UNSIGNED (op1))
ret = "ori\t%0,%%0,%x1\t\t\t# %1";
else if (SMALL_INT (op1))
ret = "addiu\t%0,%%0,%1\t\t\t# %1";
else
ret = "lui\t%0,%X1\t\t\t# %1\n\tori\t%0,%0,%x1";
}
}
else if (code1 == CONST_DOUBLE && mode == SFmode)
{
if (op1 == CONST0_RTX (SFmode))
{
if (GP_REG_P (regno0))
ret = "or\t%0,%%0,%.";
}
else
{
delay = DELAY_LOAD;
ret = "li.s\t%0,%1";
}
}
else if (code1 == LABEL_REF)
{
if (TARGET_STATS)
iq2000_count_memory_refs (op1, 1);
ret = "la\t%0,%a1";
}
else if (code1 == SYMBOL_REF || code1 == CONST)
{
if (TARGET_STATS)
iq2000_count_memory_refs (op1, 1);
ret = "la\t%0,%a1";
}
else if (code1 == PLUS)
{
rtx add_op0 = XEXP (op1, 0);
rtx add_op1 = XEXP (op1, 1);
if (GET_CODE (XEXP (op1, 1)) == REG
&& GET_CODE (XEXP (op1, 0)) == CONST_INT)
add_op0 = XEXP (op1, 1), add_op1 = XEXP (op1, 0);
operands[2] = add_op0;
operands[3] = add_op1;
ret = "add%:\t%0,%2,%3";
}
else if (code1 == HIGH)
{
operands[1] = XEXP (op1, 0);
ret = "lui\t%0,%%hi(%1)";
}
}
else if (code0 == MEM)
{
if (TARGET_STATS)
iq2000_count_memory_refs (op0, 1);
if (code1 == REG)
{
int regno1 = REGNO (op1) + subreg_offset1;
if (GP_REG_P (regno1))
{
switch (mode)
{
case SFmode: ret = "sw\t%1,%0"; break;
case SImode: ret = "sw\t%1,%0"; break;
case HImode: ret = "sh\t%1,%0"; break;
case QImode: ret = "sb\t%1,%0"; break;
default: break;
}
}
}
else if (code1 == CONST_INT && INTVAL (op1) == 0)
{
switch (mode)
{
case SFmode: ret = "sw\t%z1,%0"; break;
case SImode: ret = "sw\t%z1,%0"; break;
case HImode: ret = "sh\t%z1,%0"; break;
case QImode: ret = "sb\t%z1,%0"; break;
default: break;
}
}
else if (code1 == CONST_DOUBLE && op1 == CONST0_RTX (mode))
{
switch (mode)
{
case SFmode: ret = "sw\t%.,%0"; break;
case SImode: ret = "sw\t%.,%0"; break;
case HImode: ret = "sh\t%.,%0"; break;
case QImode: ret = "sb\t%.,%0"; break;
default: break;
}
}
}
if (ret == 0)
{
abort_with_insn (insn, "Bad move");
return 0;
}
if (delay != DELAY_NONE)
return iq2000_fill_delay_slot (ret, delay, operands, insn);
return ret;
}
static int
iq2000_address_cost (rtx addr)
{
switch (GET_CODE (addr))
{
case LO_SUM:
return 1;
case LABEL_REF:
return 2;
case CONST:
{
rtx offset = const0_rtx;
addr = eliminate_constant_term (XEXP (addr, 0), & offset);
if (GET_CODE (addr) == LABEL_REF)
return 2;
if (GET_CODE (addr) != SYMBOL_REF)
return 4;
if (! SMALL_INT (offset))
return 2;
}
case SYMBOL_REF:
return SYMBOL_REF_FLAG (addr) ? 1 : 2;
case PLUS:
{
rtx plus0 = XEXP (addr, 0);
rtx plus1 = XEXP (addr, 1);
if (GET_CODE (plus0) != REG && GET_CODE (plus1) == REG)
plus0 = XEXP (addr, 1), plus1 = XEXP (addr, 0);
if (GET_CODE (plus0) != REG)
break;
switch (GET_CODE (plus1))
{
case CONST_INT:
return SMALL_INT (plus1) ? 1 : 2;
case CONST:
case SYMBOL_REF:
case LABEL_REF:
case HIGH:
case LO_SUM:
return iq2000_address_cost (plus1) + 1;
default:
break;
}
}
default:
break;
}
return 4;
}
static enum internal_test
map_test_to_internal_test (enum rtx_code test_code)
{
enum internal_test test = ITEST_MAX;
switch (test_code)
{
case EQ: test = ITEST_EQ; break;
case NE: test = ITEST_NE; break;
case GT: test = ITEST_GT; break;
case GE: test = ITEST_GE; break;
case LT: test = ITEST_LT; break;
case LE: test = ITEST_LE; break;
case GTU: test = ITEST_GTU; break;
case GEU: test = ITEST_GEU; break;
case LTU: test = ITEST_LTU; break;
case LEU: test = ITEST_LEU; break;
default: break;
}
return test;
}
rtx
gen_int_relational (enum rtx_code test_code, rtx result, rtx cmp0, rtx cmp1,
int *p_invert)
{
struct cmp_info
{
enum rtx_code test_code;
int const_low;
int const_high;
int const_add;
int reverse_regs;
int invert_const;
int invert_reg;
int unsignedp;
};
static struct cmp_info info[ (int)ITEST_MAX ] =
{
{ XOR, 0, 65535, 0, 0, 0, 0, 0 },
{ XOR, 0, 65535, 0, 0, 1, 1, 0 },
{ LT, -32769, 32766, 1, 1, 1, 0, 0 },
{ LT, -32768, 32767, 0, 0, 1, 1, 0 },
{ LT, -32768, 32767, 0, 0, 0, 0, 0 },
{ LT, -32769, 32766, 1, 1, 0, 1, 0 },
{ LTU, -32769, 32766, 1, 1, 1, 0, 1 },
{ LTU, -32768, 32767, 0, 0, 1, 1, 1 },
{ LTU, -32768, 32767, 0, 0, 0, 0, 1 },
{ LTU, -32769, 32766, 1, 1, 0, 1, 1 },
};
enum internal_test test;
enum machine_mode mode;
struct cmp_info *p_info;
int branch_p;
int eqne_p;
int invert;
rtx reg;
rtx reg2;
test = map_test_to_internal_test (test_code);
if (test == ITEST_MAX)
abort ();
p_info = &info[(int) test];
eqne_p = (p_info->test_code == XOR);
mode = GET_MODE (cmp0);
if (mode == VOIDmode)
mode = GET_MODE (cmp1);
branch_p = (result == 0);
if (branch_p)
{
if (GET_CODE (cmp0) == REG || GET_CODE (cmp0) == SUBREG)
{
if (GET_CODE (cmp1) == CONST_INT && INTVAL (cmp1) == 0)
return 0;
if (eqne_p)
return 0;
}
result = gen_reg_rtx (mode);
}
if (GET_CODE (cmp0) == CONST_INT)
cmp0 = force_reg (mode, cmp0);
if (GET_CODE (cmp1) == CONST_INT)
{
HOST_WIDE_INT value = INTVAL (cmp1);
if (value < p_info->const_low
|| value > p_info->const_high)
cmp1 = force_reg (mode, cmp1);
}
invert = (GET_CODE (cmp1) == CONST_INT
? p_info->invert_const : p_info->invert_reg);
if (p_invert != (int *)0)
{
*p_invert = invert;
invert = 0;
}
if (GET_CODE (cmp1) == CONST_INT)
{
if (p_info->const_add != 0)
{
HOST_WIDE_INT new = INTVAL (cmp1) + p_info->const_add;
if ((p_info->unsignedp
? (unsigned HOST_WIDE_INT) new >
(unsigned HOST_WIDE_INT) INTVAL (cmp1)
: new > INTVAL (cmp1))
!= (p_info->const_add > 0))
{
emit_move_insn (result, invert ? const0_rtx : const_true_rtx);
return result;
}
else
cmp1 = GEN_INT (new);
}
}
else if (p_info->reverse_regs)
{
rtx temp = cmp0;
cmp0 = cmp1;
cmp1 = temp;
}
if (test == ITEST_NE && GET_CODE (cmp1) == CONST_INT && INTVAL (cmp1) == 0)
reg = cmp0;
else
{
reg = (invert || eqne_p) ? gen_reg_rtx (mode) : result;
convert_move (reg, gen_rtx_fmt_ee (p_info->test_code, mode, cmp0, cmp1), 0);
}
if (test == ITEST_NE)
{
convert_move (result, gen_rtx_GTU (mode, reg, const0_rtx), 0);
if (p_invert != NULL)
*p_invert = 0;
invert = 0;
}
else if (test == ITEST_EQ)
{
reg2 = invert ? gen_reg_rtx (mode) : result;
convert_move (reg2, gen_rtx_LTU (mode, reg, const1_rtx), 0);
reg = reg2;
}
if (invert)
{
rtx one;
one = const1_rtx;
convert_move (result, gen_rtx_XOR (mode, reg, one), 0);
}
return result;
}
void
gen_conditional_branch (rtx operands[], enum rtx_code test_code)
{
enum cmp_type type = branch_type;
rtx cmp0 = branch_cmp[0];
rtx cmp1 = branch_cmp[1];
enum machine_mode mode;
rtx reg;
int invert;
rtx label1, label2;
switch (type)
{
case CMP_SI:
case CMP_DI:
mode = type == CMP_SI ? SImode : DImode;
invert = 0;
reg = gen_int_relational (test_code, NULL_RTX, cmp0, cmp1, &invert);
if (reg)
{
cmp0 = reg;
cmp1 = const0_rtx;
test_code = NE;
}
else if (GET_CODE (cmp1) == CONST_INT && INTVAL (cmp1) != 0)
cmp1 = force_reg (mode, cmp1);
break;
case CMP_SF:
case CMP_DF:
reg = gen_reg_rtx (CCmode);
emit_insn (gen_rtx_SET (VOIDmode, reg,
gen_rtx_fmt_ee (test_code == NE ? EQ : test_code,
CCmode, cmp0, cmp1)));
test_code = test_code == NE ? EQ : NE;
mode = CCmode;
cmp0 = reg;
cmp1 = const0_rtx;
invert = 0;
break;
default:
abort_with_insn (gen_rtx_fmt_ee (test_code, VOIDmode, cmp0, cmp1),
"bad test");
}
label1 = gen_rtx_LABEL_REF (VOIDmode, operands[0]);
label2 = pc_rtx;
if (invert)
{
label2 = label1;
label1 = pc_rtx;
}
emit_jump_insn (gen_rtx_SET (VOIDmode, pc_rtx,
gen_rtx_IF_THEN_ELSE (VOIDmode,
gen_rtx_fmt_ee (test_code,
mode,
cmp0, cmp1),
label1, label2)));
}
void
init_cumulative_args (CUMULATIVE_ARGS *cum, tree fntype,
rtx libname ATTRIBUTE_UNUSED)
{
static CUMULATIVE_ARGS zero_cum;
tree param;
tree next_param;
if (TARGET_DEBUG_D_MODE)
{
fprintf (stderr,
"\ninit_cumulative_args, fntype = 0x%.8lx", (long) fntype);
if (!fntype)
fputc ('\n', stderr);
else
{
tree ret_type = TREE_TYPE (fntype);
fprintf (stderr, ", fntype code = %s, ret code = %s\n",
tree_code_name[(int)TREE_CODE (fntype)],
tree_code_name[(int)TREE_CODE (ret_type)]);
}
}
*cum = zero_cum;
for (param = fntype ? TYPE_ARG_TYPES (fntype) : 0;
param != 0; param = next_param)
{
next_param = TREE_CHAIN (param);
if (next_param == 0 && TREE_VALUE (param) != void_type_node)
cum->gp_reg_found = 1;
}
}
void
function_arg_advance (CUMULATIVE_ARGS *cum, enum machine_mode mode, tree type,
int named)
{
if (TARGET_DEBUG_D_MODE)
{
fprintf (stderr,
"function_adv({gp reg found = %d, arg # = %2d, words = %2d}, %4s, ",
cum->gp_reg_found, cum->arg_number, cum->arg_words,
GET_MODE_NAME (mode));
fprintf (stderr, HOST_PTR_PRINTF, (const PTR) type);
fprintf (stderr, ", %d )\n\n", named);
}
cum->arg_number++;
switch (mode)
{
case VOIDmode:
break;
default:
if (GET_MODE_CLASS (mode) != MODE_COMPLEX_INT
&& GET_MODE_CLASS (mode) != MODE_COMPLEX_FLOAT)
abort ();
cum->gp_reg_found = 1;
cum->arg_words += ((GET_MODE_SIZE (mode) + UNITS_PER_WORD - 1)
/ UNITS_PER_WORD);
break;
case BLKmode:
cum->gp_reg_found = 1;
cum->arg_words += ((int_size_in_bytes (type) + UNITS_PER_WORD - 1)
/ UNITS_PER_WORD);
break;
case SFmode:
cum->arg_words ++;
if (! cum->gp_reg_found && cum->arg_number <= 2)
cum->fp_code += 1 << ((cum->arg_number - 1) * 2);
break;
case DFmode:
cum->arg_words += 2;
if (! cum->gp_reg_found && cum->arg_number <= 2)
cum->fp_code += 2 << ((cum->arg_number - 1) * 2);
break;
case DImode:
cum->gp_reg_found = 1;
cum->arg_words += 2;
break;
case QImode:
case HImode:
case SImode:
cum->gp_reg_found = 1;
cum->arg_words ++;
break;
}
}
struct rtx_def *
function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode, tree type,
int named)
{
rtx ret;
int regbase = -1;
int bias = 0;
unsigned int *arg_words = &cum->arg_words;
int struct_p = (type != 0
&& (TREE_CODE (type) == RECORD_TYPE
|| TREE_CODE (type) == UNION_TYPE
|| TREE_CODE (type) == QUAL_UNION_TYPE));
if (TARGET_DEBUG_D_MODE)
{
fprintf (stderr,
"function_arg( {gp reg found = %d, arg # = %2d, words = %2d}, %4s, ",
cum->gp_reg_found, cum->arg_number, cum->arg_words,
GET_MODE_NAME (mode));
fprintf (stderr, HOST_PTR_PRINTF, (const PTR) type);
fprintf (stderr, ", %d ) = ", named);
}
cum->last_arg_fp = 0;
switch (mode)
{
case SFmode:
regbase = GP_ARG_FIRST;
break;
case DFmode:
cum->arg_words += cum->arg_words & 1;
regbase = GP_ARG_FIRST;
break;
default:
if (GET_MODE_CLASS (mode) != MODE_COMPLEX_INT
&& GET_MODE_CLASS (mode) != MODE_COMPLEX_FLOAT)
abort ();
case BLKmode:
if (type != NULL_TREE && TYPE_ALIGN (type) > (unsigned) BITS_PER_WORD)
cum->arg_words += (cum->arg_words & 1);
regbase = GP_ARG_FIRST;
break;
case VOIDmode:
case QImode:
case HImode:
case SImode:
regbase = GP_ARG_FIRST;
break;
case DImode:
cum->arg_words += (cum->arg_words & 1);
regbase = GP_ARG_FIRST;
}
if (*arg_words >= (unsigned) MAX_ARGS_IN_REGISTERS)
{
if (TARGET_DEBUG_D_MODE)
fprintf (stderr, "<stack>%s\n", struct_p ? ", [struct]" : "");
ret = 0;
}
else
{
if (regbase == -1)
abort ();
if (! type || TREE_CODE (type) != RECORD_TYPE
|| ! named || ! TYPE_SIZE_UNIT (type)
|| ! host_integerp (TYPE_SIZE_UNIT (type), 1))
ret = gen_rtx_REG (mode, regbase + *arg_words + bias);
else
{
tree field;
for (field = TYPE_FIELDS (type); field; field = TREE_CHAIN (field))
if (TREE_CODE (field) == FIELD_DECL
&& TREE_CODE (TREE_TYPE (field)) == REAL_TYPE
&& TYPE_PRECISION (TREE_TYPE (field)) == BITS_PER_WORD
&& host_integerp (bit_position (field), 0)
&& int_bit_position (field) % BITS_PER_WORD == 0)
break;
if (! field || mode == DFmode)
ret = gen_rtx_REG (mode, regbase + *arg_words + bias);
else
{
unsigned int chunks;
HOST_WIDE_INT bitpos;
unsigned int regno;
unsigned int i;
chunks
= tree_low_cst (TYPE_SIZE_UNIT (type), 1) / UNITS_PER_WORD;
if (chunks + *arg_words + bias > (unsigned) MAX_ARGS_IN_REGISTERS)
chunks = MAX_ARGS_IN_REGISTERS - *arg_words - bias;
ret = gen_rtx_PARALLEL (mode, rtvec_alloc (chunks));
bitpos = 0;
regno = regbase + *arg_words + bias;
field = TYPE_FIELDS (type);
for (i = 0; i < chunks; i++)
{
rtx reg;
for (; field; field = TREE_CHAIN (field))
if (TREE_CODE (field) == FIELD_DECL
&& int_bit_position (field) >= bitpos)
break;
if (field
&& int_bit_position (field) == bitpos
&& TREE_CODE (TREE_TYPE (field)) == REAL_TYPE
&& TYPE_PRECISION (TREE_TYPE (field)) == BITS_PER_WORD)
reg = gen_rtx_REG (DFmode, regno++);
else
reg = gen_rtx_REG (word_mode, regno);
XVECEXP (ret, 0, i)
= gen_rtx_EXPR_LIST (VOIDmode, reg,
GEN_INT (bitpos / BITS_PER_UNIT));
bitpos += 64;
regno++;
}
}
}
if (TARGET_DEBUG_D_MODE)
fprintf (stderr, "%s%s\n", reg_names[regbase + *arg_words + bias],
struct_p ? ", [struct]" : "");
}
if (mode == VOIDmode)
{
if (cum->num_adjusts > 0)
ret = gen_rtx_PARALLEL ((enum machine_mode) cum->fp_code,
gen_rtvec_v (cum->num_adjusts, cum->adjust));
}
return ret;
}
static int
iq2000_arg_partial_bytes (CUMULATIVE_ARGS *cum, enum machine_mode mode,
tree type ATTRIBUTE_UNUSED,
bool named ATTRIBUTE_UNUSED)
{
if (mode == DImode && cum->arg_words == MAX_ARGS_IN_REGISTERS - 1)
{
if (TARGET_DEBUG_D_MODE)
fprintf (stderr, "iq2000_arg_partial_bytes=%d\n", UNITS_PER_WORD);
return UNITS_PER_WORD;
}
return 0;
}
void
iq2000_va_start (tree valist, rtx nextarg)
{
int int_arg_words;
int gpr_save_area_size;
int_arg_words = current_function_args_info.arg_words;
if (int_arg_words < 8 )
gpr_save_area_size = (8 - int_arg_words) * UNITS_PER_WORD;
else
gpr_save_area_size = 0;
nextarg = plus_constant (nextarg, - gpr_save_area_size);
std_expand_builtin_va_start (valist, nextarg);
}
static struct machine_function *
iq2000_init_machine_status (void)
{
struct machine_function *f;
f = ggc_alloc_cleared (sizeof (struct machine_function));
return f;
}
static enum processor_type
iq2000_parse_cpu (const char * cpu_string)
{
const char *p = cpu_string;
enum processor_type cpu;
cpu = PROCESSOR_DEFAULT;
switch (p[2])
{
case '1':
if (!strcmp (p, "iq10"))
cpu = PROCESSOR_IQ10;
break;
case '2':
if (!strcmp (p, "iq2000"))
cpu = PROCESSOR_IQ2000;
break;
}
return cpu;
}
void
override_options (void)
{
enum processor_type iq2000_cpu;
target_flags &= ~MASK_GPOPT;
iq2000_isa = IQ2000_ISA_DEFAULT;
if (iq2000_cpu_string != 0)
{
iq2000_cpu = iq2000_parse_cpu (iq2000_cpu_string);
if (iq2000_cpu == PROCESSOR_DEFAULT)
{
error ("bad value (%s) for -mcpu= switch", iq2000_arch_string);
iq2000_cpu_string = "default";
}
iq2000_arch = iq2000_cpu;
iq2000_tune = iq2000_cpu;
}
if (iq2000_arch_string == 0
|| ! strcmp (iq2000_arch_string, "default")
|| ! strcmp (iq2000_arch_string, "DEFAULT"))
{
switch (iq2000_isa)
{
default:
iq2000_arch_string = "iq2000";
iq2000_arch = PROCESSOR_IQ2000;
break;
}
}
else
{
iq2000_arch = iq2000_parse_cpu (iq2000_arch_string);
if (iq2000_arch == PROCESSOR_DEFAULT)
{
error ("bad value (%s) for -march= switch", iq2000_arch_string);
iq2000_arch_string = "default";
}
if (iq2000_arch == PROCESSOR_IQ10)
{
error ("The compiler does not support -march=%s.", iq2000_arch_string);
iq2000_arch_string = "default";
}
}
iq2000_print_operand_punct['?'] = 1;
iq2000_print_operand_punct['#'] = 1;
iq2000_print_operand_punct['&'] = 1;
iq2000_print_operand_punct['!'] = 1;
iq2000_print_operand_punct['*'] = 1;
iq2000_print_operand_punct['@'] = 1;
iq2000_print_operand_punct['.'] = 1;
iq2000_print_operand_punct['('] = 1;
iq2000_print_operand_punct[')'] = 1;
iq2000_print_operand_punct['['] = 1;
iq2000_print_operand_punct[']'] = 1;
iq2000_print_operand_punct['<'] = 1;
iq2000_print_operand_punct['>'] = 1;
iq2000_print_operand_punct['{'] = 1;
iq2000_print_operand_punct['}'] = 1;
iq2000_print_operand_punct['^'] = 1;
iq2000_print_operand_punct['$'] = 1;
iq2000_print_operand_punct['+'] = 1;
iq2000_print_operand_punct['~'] = 1;
gpr_mode = SImode;
init_machine_status = iq2000_init_machine_status;
}
HOST_WIDE_INT
iq2000_debugger_offset (rtx addr, HOST_WIDE_INT offset)
{
rtx offset2 = const0_rtx;
rtx reg = eliminate_constant_term (addr, & offset2);
if (offset == 0)
offset = INTVAL (offset2);
if (reg == stack_pointer_rtx || reg == frame_pointer_rtx
|| reg == hard_frame_pointer_rtx)
{
HOST_WIDE_INT frame_size = (!cfun->machine->initialized)
? compute_frame_size (get_frame_size ())
: cfun->machine->total_size;
offset = offset - frame_size;
}
return offset;
}
void
final_prescan_insn (rtx insn, rtx opvec[] ATTRIBUTE_UNUSED,
int noperands ATTRIBUTE_UNUSED)
{
if (dslots_number_nops > 0)
{
rtx pattern = PATTERN (insn);
int length = get_attr_length (insn);
if (length == 0
|| (iq2000_load_reg != 0 && reg_mentioned_p (iq2000_load_reg, pattern))
|| (iq2000_load_reg2 != 0 && reg_mentioned_p (iq2000_load_reg2, pattern))
|| (iq2000_load_reg3 != 0 && reg_mentioned_p (iq2000_load_reg3, pattern))
|| (iq2000_load_reg4 != 0
&& reg_mentioned_p (iq2000_load_reg4, pattern)))
fputs ("\tnop\n", asm_out_file);
else
dslots_load_filled ++;
while (--dslots_number_nops > 0)
fputs ("\tnop\n", asm_out_file);
iq2000_load_reg = 0;
iq2000_load_reg2 = 0;
iq2000_load_reg3 = 0;
iq2000_load_reg4 = 0;
}
if ( (GET_CODE (insn) == JUMP_INSN
|| GET_CODE (insn) == CALL_INSN
|| (GET_CODE (PATTERN (insn)) == RETURN))
&& NEXT_INSN (PREV_INSN (insn)) == insn)
{
rtx nop_insn = emit_insn_after (gen_nop (), insn);
INSN_ADDRESSES_NEW (nop_insn, -1);
}
if (TARGET_STATS
&& (GET_CODE (insn) == JUMP_INSN || GET_CODE (insn) == CALL_INSN))
dslots_jump_total ++;
}
HOST_WIDE_INT
compute_frame_size (HOST_WIDE_INT size)
{
int regno;
HOST_WIDE_INT total_size;
HOST_WIDE_INT var_size;
HOST_WIDE_INT args_size;
HOST_WIDE_INT extra_size;
HOST_WIDE_INT gp_reg_rounded;
HOST_WIDE_INT gp_reg_size;
HOST_WIDE_INT fp_reg_size;
long mask;
int fp_inc;
long fp_bits;
gp_reg_size = 0;
fp_reg_size = 0;
mask = 0;
extra_size = IQ2000_STACK_ALIGN ((0));
var_size = IQ2000_STACK_ALIGN (size);
args_size = IQ2000_STACK_ALIGN (current_function_outgoing_args_size);
if (args_size == 0 && current_function_calls_alloca)
args_size = 4 * UNITS_PER_WORD;
total_size = var_size + args_size + extra_size;
for (regno = GP_REG_FIRST; regno <= GP_REG_LAST; regno++)
{
if (MUST_SAVE_REGISTER (regno))
{
gp_reg_size += GET_MODE_SIZE (gpr_mode);
mask |= 1L << (regno - GP_REG_FIRST);
}
}
if (current_function_calls_eh_return)
{
unsigned int i;
for (i = 0; ; ++i)
{
regno = EH_RETURN_DATA_REGNO (i);
if (regno == (int) INVALID_REGNUM)
break;
gp_reg_size += GET_MODE_SIZE (gpr_mode);
mask |= 1L << (regno - GP_REG_FIRST);
}
}
fp_inc = 2;
fp_bits = 3;
gp_reg_rounded = IQ2000_STACK_ALIGN (gp_reg_size);
total_size += gp_reg_rounded + IQ2000_STACK_ALIGN (fp_reg_size);
if (total_size == extra_size
&& ! profile_flag)
total_size = extra_size = 0;
total_size += IQ2000_STACK_ALIGN (current_function_pretend_args_size);
cfun->machine->total_size = total_size;
cfun->machine->var_size = var_size;
cfun->machine->args_size = args_size;
cfun->machine->extra_size = extra_size;
cfun->machine->gp_reg_size = gp_reg_size;
cfun->machine->fp_reg_size = fp_reg_size;
cfun->machine->mask = mask;
cfun->machine->initialized = reload_completed;
cfun->machine->num_gp = gp_reg_size / UNITS_PER_WORD;
if (mask)
{
unsigned long offset;
offset = (args_size + extra_size + var_size
+ gp_reg_size - GET_MODE_SIZE (gpr_mode));
cfun->machine->gp_sp_offset = offset;
cfun->machine->gp_save_offset = offset - total_size;
}
else
{
cfun->machine->gp_sp_offset = 0;
cfun->machine->gp_save_offset = 0;
}
cfun->machine->fp_sp_offset = 0;
cfun->machine->fp_save_offset = 0;
return total_size;
}
int
iq2000_initial_elimination_offset (int from, int to ATTRIBUTE_UNUSED)
{
int offset;
compute_frame_size (get_frame_size ());
if ((from) == FRAME_POINTER_REGNUM)
(offset) = 0;
else if ((from) == ARG_POINTER_REGNUM)
(offset) = (cfun->machine->total_size);
else if ((from) == RETURN_ADDRESS_POINTER_REGNUM)
{
if (leaf_function_p ())
(offset) = 0;
else (offset) = cfun->machine->gp_sp_offset
+ ((UNITS_PER_WORD - (POINTER_SIZE / BITS_PER_UNIT))
* (BYTES_BIG_ENDIAN != 0));
}
return offset;
}
#define BITSET_P(VALUE,BIT) (((VALUE) & (1L << (BIT))) != 0)
static rtx
iq2000_add_large_offset_to_sp (HOST_WIDE_INT offset)
{
rtx reg = gen_rtx_REG (Pmode, IQ2000_TEMP2_REGNUM);
rtx offset_rtx = GEN_INT (offset);
emit_move_insn (reg, offset_rtx);
emit_insn (gen_addsi3 (reg, reg, stack_pointer_rtx));
return reg;
}
static void
iq2000_annotate_frame_insn (rtx insn, rtx dwarf_pattern)
{
RTX_FRAME_RELATED_P (insn) = 1;
REG_NOTES (insn) = alloc_EXPR_LIST (REG_FRAME_RELATED_EXPR,
dwarf_pattern,
REG_NOTES (insn));
}
static void
iq2000_emit_frame_related_store (rtx mem, rtx reg, HOST_WIDE_INT offset)
{
rtx dwarf_address = plus_constant (stack_pointer_rtx, offset);
rtx dwarf_mem = gen_rtx_MEM (GET_MODE (reg), dwarf_address);
iq2000_annotate_frame_insn (emit_move_insn (mem, reg),
gen_rtx_SET (GET_MODE (reg), dwarf_mem, reg));
}
static void
save_restore_insns (int store_p)
{
long mask = cfun->machine->mask;
int regno;
rtx base_reg_rtx;
HOST_WIDE_INT base_offset;
HOST_WIDE_INT gp_offset;
HOST_WIDE_INT end_offset;
if (frame_pointer_needed
&& ! BITSET_P (mask, HARD_FRAME_POINTER_REGNUM - GP_REG_FIRST))
abort ();
if (mask == 0)
{
base_reg_rtx = 0, base_offset = 0;
return;
}
gp_offset = cfun->machine->gp_sp_offset;
end_offset
= gp_offset - (cfun->machine->gp_reg_size
- GET_MODE_SIZE (gpr_mode));
if (gp_offset < 0 || end_offset < 0)
internal_error
("gp_offset (%ld) or end_offset (%ld) is less than zero.",
(long) gp_offset, (long) end_offset);
else if (gp_offset < 32768)
base_reg_rtx = stack_pointer_rtx, base_offset = 0;
else
{
int regno;
int reg_save_count = 0;
for (regno = GP_REG_LAST; regno >= GP_REG_FIRST; regno--)
if (BITSET_P (mask, regno - GP_REG_FIRST)) reg_save_count += 1;
base_offset = gp_offset - ((reg_save_count - 1) * 4);
base_reg_rtx = iq2000_add_large_offset_to_sp (base_offset);
}
for (regno = GP_REG_LAST; regno >= GP_REG_FIRST; regno--)
{
if (BITSET_P (mask, regno - GP_REG_FIRST))
{
rtx reg_rtx;
rtx mem_rtx
= gen_rtx_MEM (gpr_mode,
gen_rtx_PLUS (Pmode, base_reg_rtx,
GEN_INT (gp_offset - base_offset)));
reg_rtx = gen_rtx_REG (gpr_mode, regno);
if (store_p)
iq2000_emit_frame_related_store (mem_rtx, reg_rtx, gp_offset);
else
{
emit_move_insn (reg_rtx, mem_rtx);
}
gp_offset -= GET_MODE_SIZE (gpr_mode);
}
}
}
void
iq2000_expand_prologue (void)
{
int regno;
HOST_WIDE_INT tsize;
int last_arg_is_vararg_marker = 0;
tree fndecl = current_function_decl;
tree fntype = TREE_TYPE (fndecl);
tree fnargs = DECL_ARGUMENTS (fndecl);
rtx next_arg_reg;
int i;
tree next_arg;
tree cur_arg;
CUMULATIVE_ARGS args_so_far;
int store_args_on_stack = (iq2000_can_use_return_insn ());
if (aggregate_value_p (DECL_RESULT (fndecl), fndecl)
&& ! current_function_returns_pcc_struct
&& targetm.calls.struct_value_rtx (TREE_TYPE (fndecl), 1) == 0)
{
tree type = build_pointer_type (fntype);
tree function_result_decl = build_decl (PARM_DECL, NULL_TREE, type);
DECL_ARG_TYPE (function_result_decl) = type;
TREE_CHAIN (function_result_decl) = fnargs;
fnargs = function_result_decl;
}
INIT_CUMULATIVE_ARGS (args_so_far, fntype, NULL_RTX, 0, 0);
regno = GP_ARG_FIRST;
for (cur_arg = fnargs; cur_arg != 0; cur_arg = next_arg)
{
tree passed_type = DECL_ARG_TYPE (cur_arg);
enum machine_mode passed_mode = TYPE_MODE (passed_type);
rtx entry_parm;
if (TREE_ADDRESSABLE (passed_type))
{
passed_type = build_pointer_type (passed_type);
passed_mode = Pmode;
}
entry_parm = FUNCTION_ARG (args_so_far, passed_mode, passed_type, 1);
FUNCTION_ARG_ADVANCE (args_so_far, passed_mode, passed_type, 1);
next_arg = TREE_CHAIN (cur_arg);
if (entry_parm && store_args_on_stack)
{
if (next_arg == 0
&& DECL_NAME (cur_arg)
&& ((0 == strcmp (IDENTIFIER_POINTER (DECL_NAME (cur_arg)),
"__builtin_va_alist"))
|| (0 == strcmp (IDENTIFIER_POINTER (DECL_NAME (cur_arg)),
"va_alist"))))
{
last_arg_is_vararg_marker = 1;
break;
}
else
{
int words;
if (GET_CODE (entry_parm) != REG)
abort ();
if (GET_MODE (entry_parm) == BLKmode)
words = (int_size_in_bytes (passed_type) + 3) / 4;
else
words = (GET_MODE_SIZE (GET_MODE (entry_parm)) + 3) / 4;
regno = REGNO (entry_parm) + words - 1;
}
}
else
{
regno = GP_ARG_LAST+1;
break;
}
}
next_arg_reg = FUNCTION_ARG (args_so_far, VOIDmode, void_type_node, 1);
if (next_arg_reg != 0 && GET_CODE (next_arg_reg) == PARALLEL)
{
rtvec adjust = XVEC (next_arg_reg, 0);
int num = GET_NUM_ELEM (adjust);
for (i = 0; i < num; i++)
{
rtx insn, pattern;
pattern = RTVEC_ELT (adjust, i);
if (GET_CODE (pattern) != SET
|| GET_CODE (SET_SRC (pattern)) != ASHIFT)
abort_with_insn (pattern, "Insn is not a shift");
PUT_CODE (SET_SRC (pattern), ASHIFTRT);
insn = emit_insn (pattern);
REG_NOTES(insn) = gen_rtx_EXPR_LIST (REG_MAYBE_DEAD, NULL_RTX,
REG_NOTES (insn));
}
}
tsize = compute_frame_size (get_frame_size ());
if (store_args_on_stack
&& ((TYPE_ARG_TYPES (fntype) != 0
&& (TREE_VALUE (tree_last (TYPE_ARG_TYPES (fntype)))
!= void_type_node))
|| last_arg_is_vararg_marker))
{
int offset = (regno - GP_ARG_FIRST) * UNITS_PER_WORD;
rtx ptr = stack_pointer_rtx;
for (; regno <= GP_ARG_LAST; regno++)
{
if (offset != 0)
ptr = gen_rtx_PLUS (Pmode, stack_pointer_rtx, GEN_INT (offset));
emit_move_insn (gen_rtx_MEM (gpr_mode, ptr),
gen_rtx_REG (gpr_mode, regno));
offset += GET_MODE_SIZE (gpr_mode);
}
}
if (tsize > 0)
{
rtx tsize_rtx = GEN_INT (tsize);
rtx adjustment_rtx, insn, dwarf_pattern;
if (tsize > 32767)
{
adjustment_rtx = gen_rtx_REG (Pmode, IQ2000_TEMP1_REGNUM);
emit_move_insn (adjustment_rtx, tsize_rtx);
}
else
adjustment_rtx = tsize_rtx;
insn = emit_insn (gen_subsi3 (stack_pointer_rtx, stack_pointer_rtx,
adjustment_rtx));
dwarf_pattern = gen_rtx_SET (Pmode, stack_pointer_rtx,
plus_constant (stack_pointer_rtx, -tsize));
iq2000_annotate_frame_insn (insn, dwarf_pattern);
save_restore_insns (1);
if (frame_pointer_needed)
{
rtx insn = 0;
insn = emit_insn (gen_movsi (hard_frame_pointer_rtx,
stack_pointer_rtx));
if (insn)
RTX_FRAME_RELATED_P (insn) = 1;
}
}
emit_insn (gen_blockage ());
}
void
iq2000_expand_epilogue (void)
{
HOST_WIDE_INT tsize = cfun->machine->total_size;
rtx tsize_rtx = GEN_INT (tsize);
rtx tmp_rtx = (rtx)0;
if (iq2000_can_use_return_insn ())
{
emit_jump_insn (gen_return ());
return;
}
if (tsize > 32767)
{
tmp_rtx = gen_rtx_REG (Pmode, IQ2000_TEMP1_REGNUM);
emit_move_insn (tmp_rtx, tsize_rtx);
tsize_rtx = tmp_rtx;
}
if (tsize > 0)
{
if (frame_pointer_needed)
{
emit_insn (gen_blockage ());
emit_insn (gen_movsi (stack_pointer_rtx, hard_frame_pointer_rtx));
}
save_restore_insns (0);
if (current_function_calls_eh_return)
{
rtx eh_ofs = EH_RETURN_STACKADJ_RTX;
emit_insn (gen_addsi3 (eh_ofs, eh_ofs, tsize_rtx));
tsize_rtx = eh_ofs;
}
emit_insn (gen_blockage ());
if (tsize != 0 || current_function_calls_eh_return)
{
emit_insn (gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx,
tsize_rtx));
}
}
if (current_function_calls_eh_return)
{
emit_move_insn (gen_rtx_REG (Pmode, HARD_FRAME_POINTER_REGNUM),
stack_pointer_rtx);
emit_insn (gen_rtx_USE (VOIDmode, gen_rtx_REG (Pmode,
HARD_FRAME_POINTER_REGNUM)));
emit_jump_insn (gen_eh_return_internal ());
}
else
emit_jump_insn (gen_return_internal (gen_rtx_REG (Pmode,
GP_REG_FIRST + 31)));
}
void
iq2000_expand_eh_return (rtx address)
{
HOST_WIDE_INT gp_offset = cfun->machine->gp_sp_offset;
rtx scratch;
scratch = plus_constant (stack_pointer_rtx, gp_offset);
emit_move_insn (gen_rtx_MEM (GET_MODE (address), scratch), address);
}
int
iq2000_can_use_return_insn (void)
{
if (! reload_completed)
return 0;
if (regs_ever_live[31] || profile_flag)
return 0;
if (cfun->machine->initialized)
return cfun->machine->total_size == 0;
return compute_frame_size (get_frame_size ()) == 0;
}
static int
symbolic_expression_p (rtx x)
{
if (GET_CODE (x) == SYMBOL_REF)
return 1;
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 0;
}
static void
iq2000_select_rtx_section (enum machine_mode mode, rtx x ATTRIBUTE_UNUSED,
unsigned HOST_WIDE_INT align)
{
mergeable_constant_section (mode, align, 0);
}
static void
iq2000_select_section (tree decl, int reloc ATTRIBUTE_UNUSED,
unsigned HOST_WIDE_INT align ATTRIBUTE_UNUSED)
{
if (TARGET_EMBEDDED_DATA)
{
if ((TREE_CODE (decl) == VAR_DECL
&& TREE_READONLY (decl) && !TREE_SIDE_EFFECTS (decl)
&& DECL_INITIAL (decl)
&& (DECL_INITIAL (decl) == error_mark_node
|| TREE_CONSTANT (DECL_INITIAL (decl))))
|| TREE_CODE (decl) != VAR_DECL)
readonly_data_section ();
else
data_section ();
}
else
{
if ((TREE_CODE (decl) == VAR_DECL
&& TREE_READONLY (decl) && !TREE_SIDE_EFFECTS (decl)
&& DECL_INITIAL (decl)
&& (DECL_INITIAL (decl) == error_mark_node
|| TREE_CONSTANT (DECL_INITIAL (decl))))
|| TREE_CODE (decl) != VAR_DECL)
readonly_data_section ();
else
data_section ();
}
}
rtx
iq2000_function_value (tree valtype, tree func ATTRIBUTE_UNUSED)
{
int reg = GP_RETURN;
enum machine_mode mode = TYPE_MODE (valtype);
int unsignedp = TYPE_UNSIGNED (valtype);
mode = promote_mode (valtype, mode, &unsignedp, 1);
return gen_rtx_REG (mode, reg);
}
static bool
iq2000_pass_by_reference (CUMULATIVE_ARGS *cum, enum machine_mode mode,
tree type, bool named ATTRIBUTE_UNUSED)
{
int size;
if (cum && targetm.calls.must_pass_in_stack (mode, type))
{
CUMULATIVE_ARGS temp;
temp = *cum;
if (FUNCTION_ARG (temp, mode, type, named) != 0)
return 1;
}
if (type == NULL_TREE || mode == DImode || mode == DFmode)
return 0;
size = int_size_in_bytes (type);
return size == -1 || size > UNITS_PER_WORD;
}
int
iq2000_adjust_insn_length (rtx insn, int length)
{
if (simplejump_p (insn)
|| ( (GET_CODE (insn) == JUMP_INSN
|| GET_CODE (insn) == CALL_INSN)))
length += 4;
return length;
}
char *
iq2000_output_conditional_branch (rtx insn, rtx * operands, int two_operands_p,
int float_p, int inverted_p, int length)
{
static char buffer[200];
enum rtx_code code = GET_CODE (operands[0]);
int need_z_p;
const char *op1 = "%z2";
const char *op2 = (two_operands_p ? ",%z3" : ",%.");
const char *comp = (float_p ? "%F0" : "%C0");
const char *inverted_comp = (float_p ? "%W0" : "%N0");
iq2000_branch_likely = (final_sequence && INSN_ANNULLED_BRANCH_P (insn));
if (!two_operands_p)
{
switch (code)
{
case GTU:
code = NE;
break;
case LEU:
code = EQ;
break;
case GEU:
code = EQ;
op1 = "%.";
break;
case LTU:
code = NE;
op1 = "%.";
break;
default:
break;
}
}
need_z_p = (!float_p && code != EQ && code != NE);
if (need_z_p)
op2 = "";
buffer[0] = '\0';
switch (length)
{
case 4:
case 8:
if (float_p)
sprintf (buffer, "b%s%%?\t%%Z2%%1",
inverted_p ? inverted_comp : comp);
else
sprintf (buffer, "b%s%s%%?\t%s%s,%%1",
inverted_p ? inverted_comp : comp,
need_z_p ? "z" : "",
op1,
op2);
return buffer;
case 12:
case 16:
{
const char *target
= ((iq2000_branch_likely || length == 16) ? ".+16" : ".+12");
char *c;
c = strchr (buffer, '\0');
if (float_p)
sprintf (c, "b%s\t%%Z2%s",
inverted_p ? comp : inverted_comp,
target);
else
sprintf (c, "b%s%s\t%s%s,%s",
inverted_p ? comp : inverted_comp,
need_z_p ? "z" : "",
op1,
op2,
target);
strcat (c, "\n\tnop\n\tj\t%1");
if (length == 16)
strcat (buffer, "\n\tnop");
return buffer;
}
default:
abort ();
}
return 0;
}
#define def_builtin(NAME, TYPE, CODE) \
lang_hooks.builtin_function ((NAME), (TYPE), (CODE), BUILT_IN_MD, \
NULL, NULL_TREE)
static void
iq2000_init_builtins (void)
{
tree endlink = void_list_node;
tree void_ftype, void_ftype_int, void_ftype_int_int;
tree void_ftype_int_int_int;
tree int_ftype_int, int_ftype_int_int, int_ftype_int_int_int;
tree int_ftype_int_int_int_int;
void_ftype
= build_function_type (void_type_node,
tree_cons (NULL_TREE, void_type_node, endlink));
void_ftype_int
= build_function_type (void_type_node,
tree_cons (NULL_TREE, integer_type_node, endlink));
void_ftype_int_int
= build_function_type (void_type_node,
tree_cons (NULL_TREE, integer_type_node,
tree_cons (NULL_TREE, integer_type_node,
endlink)));
int_ftype_int
= build_function_type (integer_type_node,
tree_cons (NULL_TREE, integer_type_node, endlink));
int_ftype_int_int
= build_function_type (integer_type_node,
tree_cons (NULL_TREE, integer_type_node,
tree_cons (NULL_TREE, integer_type_node,
endlink)));
void_ftype_int_int_int
= build_function_type
(void_type_node,
tree_cons (NULL_TREE, integer_type_node,
tree_cons (NULL_TREE, integer_type_node,
tree_cons (NULL_TREE,
integer_type_node,
endlink))));
int_ftype_int_int_int_int
= build_function_type
(integer_type_node,
tree_cons (NULL_TREE, integer_type_node,
tree_cons (NULL_TREE, integer_type_node,
tree_cons (NULL_TREE,
integer_type_node,
tree_cons (NULL_TREE,
integer_type_node,
endlink)))));
int_ftype_int_int_int
= build_function_type
(integer_type_node,
tree_cons (NULL_TREE, integer_type_node,
tree_cons (NULL_TREE, integer_type_node,
tree_cons (NULL_TREE,
integer_type_node,
endlink))));
int_ftype_int_int_int_int
= build_function_type
(integer_type_node,
tree_cons (NULL_TREE, integer_type_node,
tree_cons (NULL_TREE, integer_type_node,
tree_cons (NULL_TREE,
integer_type_node,
tree_cons (NULL_TREE,
integer_type_node,
endlink)))));
def_builtin ("__builtin_ado16", int_ftype_int_int, IQ2000_BUILTIN_ADO16);
def_builtin ("__builtin_ram", int_ftype_int_int_int_int, IQ2000_BUILTIN_RAM);
def_builtin ("__builtin_chkhdr", void_ftype_int_int, IQ2000_BUILTIN_CHKHDR);
def_builtin ("__builtin_pkrl", void_ftype_int_int, IQ2000_BUILTIN_PKRL);
def_builtin ("__builtin_cfc0", int_ftype_int, IQ2000_BUILTIN_CFC0);
def_builtin ("__builtin_cfc1", int_ftype_int, IQ2000_BUILTIN_CFC1);
def_builtin ("__builtin_cfc2", int_ftype_int, IQ2000_BUILTIN_CFC2);
def_builtin ("__builtin_cfc3", int_ftype_int, IQ2000_BUILTIN_CFC3);
def_builtin ("__builtin_ctc0", void_ftype_int_int, IQ2000_BUILTIN_CTC0);
def_builtin ("__builtin_ctc1", void_ftype_int_int, IQ2000_BUILTIN_CTC1);
def_builtin ("__builtin_ctc2", void_ftype_int_int, IQ2000_BUILTIN_CTC2);
def_builtin ("__builtin_ctc3", void_ftype_int_int, IQ2000_BUILTIN_CTC3);
def_builtin ("__builtin_mfc0", int_ftype_int, IQ2000_BUILTIN_MFC0);
def_builtin ("__builtin_mfc1", int_ftype_int, IQ2000_BUILTIN_MFC1);
def_builtin ("__builtin_mfc2", int_ftype_int, IQ2000_BUILTIN_MFC2);
def_builtin ("__builtin_mfc3", int_ftype_int, IQ2000_BUILTIN_MFC3);
def_builtin ("__builtin_mtc0", void_ftype_int_int, IQ2000_BUILTIN_MTC0);
def_builtin ("__builtin_mtc1", void_ftype_int_int, IQ2000_BUILTIN_MTC1);
def_builtin ("__builtin_mtc2", void_ftype_int_int, IQ2000_BUILTIN_MTC2);
def_builtin ("__builtin_mtc3", void_ftype_int_int, IQ2000_BUILTIN_MTC3);
def_builtin ("__builtin_lur", void_ftype_int_int, IQ2000_BUILTIN_LUR);
def_builtin ("__builtin_rb", void_ftype_int_int, IQ2000_BUILTIN_RB);
def_builtin ("__builtin_rx", void_ftype_int_int, IQ2000_BUILTIN_RX);
def_builtin ("__builtin_srrd", void_ftype_int, IQ2000_BUILTIN_SRRD);
def_builtin ("__builtin_srwr", void_ftype_int_int, IQ2000_BUILTIN_SRWR);
def_builtin ("__builtin_wb", void_ftype_int_int, IQ2000_BUILTIN_WB);
def_builtin ("__builtin_wx", void_ftype_int_int, IQ2000_BUILTIN_WX);
def_builtin ("__builtin_luc32l", void_ftype_int_int, IQ2000_BUILTIN_LUC32L);
def_builtin ("__builtin_luc64", void_ftype_int_int, IQ2000_BUILTIN_LUC64);
def_builtin ("__builtin_luc64l", void_ftype_int_int, IQ2000_BUILTIN_LUC64L);
def_builtin ("__builtin_luk", void_ftype_int_int, IQ2000_BUILTIN_LUK);
def_builtin ("__builtin_lulck", void_ftype_int, IQ2000_BUILTIN_LULCK);
def_builtin ("__builtin_lum32", void_ftype_int_int, IQ2000_BUILTIN_LUM32);
def_builtin ("__builtin_lum32l", void_ftype_int_int, IQ2000_BUILTIN_LUM32L);
def_builtin ("__builtin_lum64", void_ftype_int_int, IQ2000_BUILTIN_LUM64);
def_builtin ("__builtin_lum64l", void_ftype_int_int, IQ2000_BUILTIN_LUM64L);
def_builtin ("__builtin_lurl", void_ftype_int_int, IQ2000_BUILTIN_LURL);
def_builtin ("__builtin_mrgb", int_ftype_int_int_int, IQ2000_BUILTIN_MRGB);
def_builtin ("__builtin_srrdl", void_ftype_int, IQ2000_BUILTIN_SRRDL);
def_builtin ("__builtin_srulck", void_ftype_int, IQ2000_BUILTIN_SRULCK);
def_builtin ("__builtin_srwru", void_ftype_int_int, IQ2000_BUILTIN_SRWRU);
def_builtin ("__builtin_trapqfl", void_ftype, IQ2000_BUILTIN_TRAPQFL);
def_builtin ("__builtin_trapqne", void_ftype, IQ2000_BUILTIN_TRAPQNE);
def_builtin ("__builtin_traprel", void_ftype_int, IQ2000_BUILTIN_TRAPREL);
def_builtin ("__builtin_wbu", void_ftype_int_int_int, IQ2000_BUILTIN_WBU);
def_builtin ("__builtin_syscall", void_ftype, IQ2000_BUILTIN_SYSCALL);
}
static rtx
expand_one_builtin (enum insn_code icode, rtx target, tree arglist,
enum rtx_code *code, int argcount)
{
rtx pat;
tree arg [5];
rtx op [5];
enum machine_mode mode [5];
int i;
mode[0] = insn_data[icode].operand[0].mode;
for (i = 0; i < argcount; i++)
{
arg[i] = TREE_VALUE (arglist);
arglist = TREE_CHAIN (arglist);
op[i] = expand_expr (arg[i], NULL_RTX, VOIDmode, 0);
mode[i] = insn_data[icode].operand[i].mode;
if (code[i] == CONST_INT && GET_CODE (op[i]) != CONST_INT)
error ("argument %qd is not a constant", i + 1);
if (code[i] == REG
&& ! (*insn_data[icode].operand[i].predicate) (op[i], mode[i]))
op[i] = copy_to_mode_reg (mode[i], op[i]);
}
if (insn_data[icode].operand[0].constraint[0] == '=')
{
if (target == 0
|| GET_MODE (target) != mode[0]
|| ! (*insn_data[icode].operand[0].predicate) (target, mode[0]))
target = gen_reg_rtx (mode[0]);
}
else
target = 0;
switch (argcount)
{
case 0:
pat = GEN_FCN (icode) (target);
case 1:
if (target)
pat = GEN_FCN (icode) (target, op[0]);
else
pat = GEN_FCN (icode) (op[0]);
break;
case 2:
if (target)
pat = GEN_FCN (icode) (target, op[0], op[1]);
else
pat = GEN_FCN (icode) (op[0], op[1]);
break;
case 3:
if (target)
pat = GEN_FCN (icode) (target, op[0], op[1], op[2]);
else
pat = GEN_FCN (icode) (op[0], op[1], op[2]);
break;
case 4:
if (target)
pat = GEN_FCN (icode) (target, op[0], op[1], op[2], op[3]);
else
pat = GEN_FCN (icode) (op[0], op[1], op[2], op[3]);
break;
default:
abort ();
}
if (! pat)
return 0;
emit_insn (pat);
return target;
}
static rtx
iq2000_expand_builtin (tree exp, rtx target, rtx subtarget ATTRIBUTE_UNUSED,
enum machine_mode mode ATTRIBUTE_UNUSED,
int ignore ATTRIBUTE_UNUSED)
{
tree fndecl = TREE_OPERAND (TREE_OPERAND (exp, 0), 0);
tree arglist = TREE_OPERAND (exp, 1);
int fcode = DECL_FUNCTION_CODE (fndecl);
enum rtx_code code [5];
code[0] = REG;
code[1] = REG;
code[2] = REG;
code[3] = REG;
code[4] = REG;
switch (fcode)
{
default:
break;
case IQ2000_BUILTIN_ADO16:
return expand_one_builtin (CODE_FOR_ado16, target, arglist, code, 2);
case IQ2000_BUILTIN_RAM:
code[1] = CONST_INT;
code[2] = CONST_INT;
code[3] = CONST_INT;
return expand_one_builtin (CODE_FOR_ram, target, arglist, code, 4);
case IQ2000_BUILTIN_CHKHDR:
return expand_one_builtin (CODE_FOR_chkhdr, target, arglist, code, 2);
case IQ2000_BUILTIN_PKRL:
return expand_one_builtin (CODE_FOR_pkrl, target, arglist, code, 2);
case IQ2000_BUILTIN_CFC0:
code[0] = CONST_INT;
return expand_one_builtin (CODE_FOR_cfc0, target, arglist, code, 1);
case IQ2000_BUILTIN_CFC1:
code[0] = CONST_INT;
return expand_one_builtin (CODE_FOR_cfc1, target, arglist, code, 1);
case IQ2000_BUILTIN_CFC2:
code[0] = CONST_INT;
return expand_one_builtin (CODE_FOR_cfc2, target, arglist, code, 1);
case IQ2000_BUILTIN_CFC3:
code[0] = CONST_INT;
return expand_one_builtin (CODE_FOR_cfc3, target, arglist, code, 1);
case IQ2000_BUILTIN_CTC0:
code[1] = CONST_INT;
return expand_one_builtin (CODE_FOR_ctc0, target, arglist, code, 2);
case IQ2000_BUILTIN_CTC1:
code[1] = CONST_INT;
return expand_one_builtin (CODE_FOR_ctc1, target, arglist, code, 2);
case IQ2000_BUILTIN_CTC2:
code[1] = CONST_INT;
return expand_one_builtin (CODE_FOR_ctc2, target, arglist, code, 2);
case IQ2000_BUILTIN_CTC3:
code[1] = CONST_INT;
return expand_one_builtin (CODE_FOR_ctc3, target, arglist, code, 2);
case IQ2000_BUILTIN_MFC0:
code[0] = CONST_INT;
return expand_one_builtin (CODE_FOR_mfc0, target, arglist, code, 1);
case IQ2000_BUILTIN_MFC1:
code[0] = CONST_INT;
return expand_one_builtin (CODE_FOR_mfc1, target, arglist, code, 1);
case IQ2000_BUILTIN_MFC2:
code[0] = CONST_INT;
return expand_one_builtin (CODE_FOR_mfc2, target, arglist, code, 1);
case IQ2000_BUILTIN_MFC3:
code[0] = CONST_INT;
return expand_one_builtin (CODE_FOR_mfc3, target, arglist, code, 1);
case IQ2000_BUILTIN_MTC0:
code[1] = CONST_INT;
return expand_one_builtin (CODE_FOR_mtc0, target, arglist, code, 2);
case IQ2000_BUILTIN_MTC1:
code[1] = CONST_INT;
return expand_one_builtin (CODE_FOR_mtc1, target, arglist, code, 2);
case IQ2000_BUILTIN_MTC2:
code[1] = CONST_INT;
return expand_one_builtin (CODE_FOR_mtc2, target, arglist, code, 2);
case IQ2000_BUILTIN_MTC3:
code[1] = CONST_INT;
return expand_one_builtin (CODE_FOR_mtc3, target, arglist, code, 2);
case IQ2000_BUILTIN_LUR:
return expand_one_builtin (CODE_FOR_lur, target, arglist, code, 2);
case IQ2000_BUILTIN_RB:
return expand_one_builtin (CODE_FOR_rb, target, arglist, code, 2);
case IQ2000_BUILTIN_RX:
return expand_one_builtin (CODE_FOR_rx, target, arglist, code, 2);
case IQ2000_BUILTIN_SRRD:
return expand_one_builtin (CODE_FOR_srrd, target, arglist, code, 1);
case IQ2000_BUILTIN_SRWR:
return expand_one_builtin (CODE_FOR_srwr, target, arglist, code, 2);
case IQ2000_BUILTIN_WB:
return expand_one_builtin (CODE_FOR_wb, target, arglist, code, 2);
case IQ2000_BUILTIN_WX:
return expand_one_builtin (CODE_FOR_wx, target, arglist, code, 2);
case IQ2000_BUILTIN_LUC32L:
return expand_one_builtin (CODE_FOR_luc32l, target, arglist, code, 2);
case IQ2000_BUILTIN_LUC64:
return expand_one_builtin (CODE_FOR_luc64, target, arglist, code, 2);
case IQ2000_BUILTIN_LUC64L:
return expand_one_builtin (CODE_FOR_luc64l, target, arglist, code, 2);
case IQ2000_BUILTIN_LUK:
return expand_one_builtin (CODE_FOR_luk, target, arglist, code, 2);
case IQ2000_BUILTIN_LULCK:
return expand_one_builtin (CODE_FOR_lulck, target, arglist, code, 1);
case IQ2000_BUILTIN_LUM32:
return expand_one_builtin (CODE_FOR_lum32, target, arglist, code, 2);
case IQ2000_BUILTIN_LUM32L:
return expand_one_builtin (CODE_FOR_lum32l, target, arglist, code, 2);
case IQ2000_BUILTIN_LUM64:
return expand_one_builtin (CODE_FOR_lum64, target, arglist, code, 2);
case IQ2000_BUILTIN_LUM64L:
return expand_one_builtin (CODE_FOR_lum64l, target, arglist, code, 2);
case IQ2000_BUILTIN_LURL:
return expand_one_builtin (CODE_FOR_lurl, target, arglist, code, 2);
case IQ2000_BUILTIN_MRGB:
code[2] = CONST_INT;
return expand_one_builtin (CODE_FOR_mrgb, target, arglist, code, 3);
case IQ2000_BUILTIN_SRRDL:
return expand_one_builtin (CODE_FOR_srrdl, target, arglist, code, 1);
case IQ2000_BUILTIN_SRULCK:
return expand_one_builtin (CODE_FOR_srulck, target, arglist, code, 1);
case IQ2000_BUILTIN_SRWRU:
return expand_one_builtin (CODE_FOR_srwru, target, arglist, code, 2);
case IQ2000_BUILTIN_TRAPQFL:
return expand_one_builtin (CODE_FOR_trapqfl, target, arglist, code, 0);
case IQ2000_BUILTIN_TRAPQNE:
return expand_one_builtin (CODE_FOR_trapqne, target, arglist, code, 0);
case IQ2000_BUILTIN_TRAPREL:
return expand_one_builtin (CODE_FOR_traprel, target, arglist, code, 1);
case IQ2000_BUILTIN_WBU:
return expand_one_builtin (CODE_FOR_wbu, target, arglist, code, 3);
case IQ2000_BUILTIN_SYSCALL:
return expand_one_builtin (CODE_FOR_syscall, target, arglist, code, 0);
}
return NULL_RTX;
}
static bool
iq2000_return_in_memory (tree type, tree fntype ATTRIBUTE_UNUSED)
{
return ((int_size_in_bytes (type) > (2 * UNITS_PER_WORD))
|| (int_size_in_bytes (type) == -1));
}
static void
iq2000_setup_incoming_varargs (CUMULATIVE_ARGS *cum,
enum machine_mode mode ATTRIBUTE_UNUSED,
tree type ATTRIBUTE_UNUSED, int * pretend_size,
int no_rtl)
{
unsigned int iq2000_off = ! cum->last_arg_fp;
unsigned int iq2000_fp_off = cum->last_arg_fp;
if ((cum->arg_words < MAX_ARGS_IN_REGISTERS - iq2000_off))
{
int iq2000_save_gp_regs
= MAX_ARGS_IN_REGISTERS - cum->arg_words - iq2000_off;
int iq2000_save_fp_regs
= (MAX_ARGS_IN_REGISTERS - cum->fp_arg_words - iq2000_fp_off);
if (iq2000_save_gp_regs < 0)
iq2000_save_gp_regs = 0;
if (iq2000_save_fp_regs < 0)
iq2000_save_fp_regs = 0;
*pretend_size = ((iq2000_save_gp_regs * UNITS_PER_WORD)
+ (iq2000_save_fp_regs * UNITS_PER_FPREG));
if (! (no_rtl))
{
if (cum->arg_words < MAX_ARGS_IN_REGISTERS - iq2000_off)
{
rtx ptr, mem;
ptr = plus_constant (virtual_incoming_args_rtx,
- (iq2000_save_gp_regs
* UNITS_PER_WORD));
mem = gen_rtx_MEM (BLKmode, ptr);
move_block_from_reg
(cum->arg_words + GP_ARG_FIRST + iq2000_off,
mem,
iq2000_save_gp_regs);
}
}
}
}
void
print_operand_address (FILE * file, rtx addr)
{
if (!addr)
error ("PRINT_OPERAND_ADDRESS, null pointer");
else
switch (GET_CODE (addr))
{
case REG:
if (REGNO (addr) == ARG_POINTER_REGNUM)
abort_with_insn (addr, "Arg pointer not eliminated.");
fprintf (file, "0(%s)", reg_names [REGNO (addr)]);
break;
case LO_SUM:
{
rtx arg0 = XEXP (addr, 0);
rtx arg1 = XEXP (addr, 1);
if (GET_CODE (arg0) != REG)
abort_with_insn (addr,
"PRINT_OPERAND_ADDRESS, LO_SUM with #1 not REG.");
fprintf (file, "%%lo(");
print_operand_address (file, arg1);
fprintf (file, ")(%s)", reg_names [REGNO (arg0)]);
}
break;
case PLUS:
{
rtx reg = 0;
rtx offset = 0;
rtx arg0 = XEXP (addr, 0);
rtx arg1 = XEXP (addr, 1);
if (GET_CODE (arg0) == REG)
{
reg = arg0;
offset = arg1;
if (GET_CODE (offset) == REG)
abort_with_insn (addr, "PRINT_OPERAND_ADDRESS, 2 regs");
}
else if (GET_CODE (arg1) == REG)
reg = arg1, offset = arg0;
else if (CONSTANT_P (arg0) && CONSTANT_P (arg1))
{
output_addr_const (file, addr);
break;
}
else
abort_with_insn (addr, "PRINT_OPERAND_ADDRESS, no regs");
if (! CONSTANT_P (offset))
abort_with_insn (addr, "PRINT_OPERAND_ADDRESS, invalid insn #2");
if (REGNO (reg) == ARG_POINTER_REGNUM)
abort_with_insn (addr, "Arg pointer not eliminated.");
output_addr_const (file, offset);
fprintf (file, "(%s)", reg_names [REGNO (reg)]);
}
break;
case LABEL_REF:
case SYMBOL_REF:
case CONST_INT:
case CONST:
output_addr_const (file, addr);
if (GET_CODE (addr) == CONST_INT)
fprintf (file, "(%s)", reg_names [0]);
break;
default:
abort_with_insn (addr, "PRINT_OPERAND_ADDRESS, invalid insn #1");
break;
}
}
void
print_operand (FILE *file, rtx op, int letter)
{
enum rtx_code code;
if (PRINT_OPERAND_PUNCT_VALID_P (letter))
{
switch (letter)
{
case '?':
if (iq2000_branch_likely)
putc ('l', file);
break;
case '@':
fputs (reg_names [GP_REG_FIRST + 1], file);
break;
case '.':
fputs (reg_names [GP_REG_FIRST + 0], file);
break;
case '$':
fputs (reg_names[STACK_POINTER_REGNUM], file);
break;
case '+':
fputs (reg_names[GP_REG_FIRST + 28], file);
break;
default:
error ("PRINT_OPERAND: Unknown punctuation '%c'", letter);
break;
}
return;
}
if (! op)
{
error ("PRINT_OPERAND null pointer");
return;
}
code = GET_CODE (op);
if (code == SIGN_EXTEND)
op = XEXP (op, 0), code = GET_CODE (op);
if (letter == 'C')
switch (code)
{
case EQ: fputs ("eq", file); break;
case NE: fputs ("ne", file); break;
case GT: fputs ("gt", file); break;
case GE: fputs ("ge", file); break;
case LT: fputs ("lt", file); break;
case LE: fputs ("le", file); break;
case GTU: fputs ("ne", file); break;
case GEU: fputs ("geu", file); break;
case LTU: fputs ("ltu", file); break;
case LEU: fputs ("eq", file); break;
default:
abort_with_insn (op, "PRINT_OPERAND, invalid insn for %%C");
}
else if (letter == 'N')
switch (code)
{
case EQ: fputs ("ne", file); break;
case NE: fputs ("eq", file); break;
case GT: fputs ("le", file); break;
case GE: fputs ("lt", file); break;
case LT: fputs ("ge", file); break;
case LE: fputs ("gt", file); break;
case GTU: fputs ("leu", file); break;
case GEU: fputs ("ltu", file); break;
case LTU: fputs ("geu", file); break;
case LEU: fputs ("gtu", file); break;
default:
abort_with_insn (op, "PRINT_OPERAND, invalid insn for %%N");
}
else if (letter == 'F')
switch (code)
{
case EQ: fputs ("c1f", file); break;
case NE: fputs ("c1t", file); break;
default:
abort_with_insn (op, "PRINT_OPERAND, invalid insn for %%F");
}
else if (letter == 'W')
switch (code)
{
case EQ: fputs ("c1t", file); break;
case NE: fputs ("c1f", file); break;
default:
abort_with_insn (op, "PRINT_OPERAND, invalid insn for %%W");
}
else if (letter == 'A')
fputs (code == LABEL_REF ? "i" : "in", file);
else if (letter == 'P')
{
if (code == LABEL_REF)
output_addr_const (file, op);
else if (code != PC)
output_operand_lossage ("invalid %%P operand");
}
else if (letter == 'p')
{
int value;
if (code != CONST_INT
|| (value = exact_log2 (INTVAL (op))) < 0)
output_operand_lossage ("invalid %%p value");
fprintf (file, "%d", value);
}
else if (letter == 'Z')
{
int regnum;
if (code != REG)
abort ();
regnum = REGNO (op);
abort ();
fprintf (file, "%s,", reg_names[regnum]);
}
else if (code == REG || code == SUBREG)
{
int regnum;
if (code == REG)
regnum = REGNO (op);
else
regnum = true_regnum (op);
if ((letter == 'M' && ! WORDS_BIG_ENDIAN)
|| (letter == 'L' && WORDS_BIG_ENDIAN)
|| letter == 'D')
regnum++;
fprintf (file, "%s", reg_names[regnum]);
}
else if (code == MEM)
{
if (letter == 'D')
output_address (plus_constant (XEXP (op, 0), 4));
else
output_address (XEXP (op, 0));
}
else if (code == CONST_DOUBLE
&& GET_MODE_CLASS (GET_MODE (op)) == MODE_FLOAT)
{
char s[60];
real_to_decimal (s, CONST_DOUBLE_REAL_VALUE (op), sizeof (s), 0, 1);
fputs (s, file);
}
else if (letter == 'x' && GET_CODE (op) == CONST_INT)
fprintf (file, HOST_WIDE_INT_PRINT_HEX, 0xffff & INTVAL(op));
else if (letter == 'X' && GET_CODE(op) == CONST_INT)
fprintf (file, HOST_WIDE_INT_PRINT_HEX, 0xffff & (INTVAL (op) >> 16));
else if (letter == 'd' && GET_CODE(op) == CONST_INT)
fprintf (file, HOST_WIDE_INT_PRINT_DEC, (INTVAL(op)));
else if (letter == 'z' && GET_CODE (op) == CONST_INT && INTVAL (op) == 0)
fputs (reg_names[GP_REG_FIRST], file);
else if (letter == 'd' || letter == 'x' || letter == 'X')
output_operand_lossage ("invalid use of %%d, %%x, or %%X");
else if (letter == 'B')
fputs (code == EQ ? "z" : "n", file);
else if (letter == 'b')
fputs (code == EQ ? "n" : "z", file);
else if (letter == 'T')
fputs (code == EQ ? "f" : "t", file);
else if (letter == 't')
fputs (code == EQ ? "t" : "f", file);
else if (code == CONST && GET_CODE (XEXP (op, 0)) == REG)
{
print_operand (file, XEXP (op, 0), letter);
}
else
output_addr_const (file, op);
}
static bool
iq2000_rtx_costs (rtx x, int code, int outer_code ATTRIBUTE_UNUSED, int * total)
{
enum machine_mode mode = GET_MODE (x);
switch (code)
{
case MEM:
{
int num_words = (GET_MODE_SIZE (mode) > UNITS_PER_WORD) ? 2 : 1;
if (simple_memory_operand (x, mode))
return COSTS_N_INSNS (num_words);
* total = COSTS_N_INSNS (2 * num_words);
break;
}
case FFS:
* total = COSTS_N_INSNS (6);
break;
case AND:
case IOR:
case XOR:
case NOT:
* total = COSTS_N_INSNS (mode == DImode ? 2 : 1);
break;
case ASHIFT:
case ASHIFTRT:
case LSHIFTRT:
if (mode == DImode)
* total = COSTS_N_INSNS ((GET_CODE (XEXP (x, 1)) == CONST_INT) ? 4 : 12);
else
* total = COSTS_N_INSNS (1);
break;
case ABS:
if (mode == SFmode || mode == DFmode)
* total = COSTS_N_INSNS (1);
else
* total = COSTS_N_INSNS (4);
break;
case PLUS:
case MINUS:
if (mode == SFmode || mode == DFmode)
* total = COSTS_N_INSNS (6);
else if (mode == DImode)
* total = COSTS_N_INSNS (4);
else
* total = COSTS_N_INSNS (1);
break;
case NEG:
* total = (mode == DImode) ? 4 : 1;
break;
case MULT:
if (mode == SFmode)
* total = COSTS_N_INSNS (7);
else if (mode == DFmode)
* total = COSTS_N_INSNS (8);
else
* total = COSTS_N_INSNS (10);
break;
case DIV:
case MOD:
if (mode == SFmode)
* total = COSTS_N_INSNS (23);
else if (mode == DFmode)
* total = COSTS_N_INSNS (36);
else
* total = COSTS_N_INSNS (69);
break;
case UDIV:
case UMOD:
* total = COSTS_N_INSNS (69);
break;
case SIGN_EXTEND:
* total = COSTS_N_INSNS (2);
break;
case ZERO_EXTEND:
* total = COSTS_N_INSNS (1);
break;
case CONST_INT:
* total = 0;
break;
case LABEL_REF:
* total = COSTS_N_INSNS (2);
break;
case CONST:
{
rtx offset = const0_rtx;
rtx symref = eliminate_constant_term (XEXP (x, 0), & offset);
if (GET_CODE (symref) == LABEL_REF)
* total = COSTS_N_INSNS (2);
else if (GET_CODE (symref) != SYMBOL_REF)
* total = COSTS_N_INSNS (4);
else if (INTVAL (offset) < -32768 || INTVAL (offset) > 32767)
* total = COSTS_N_INSNS (2);
else
* total = COSTS_N_INSNS (SYMBOL_REF_FLAG (symref) ? 1 : 2);
break;
}
case SYMBOL_REF:
* total = COSTS_N_INSNS (SYMBOL_REF_FLAG (x) ? 1 : 2);
break;
case CONST_DOUBLE:
{
rtx high, low;
split_double (x, & high, & low);
* total = COSTS_N_INSNS ( (high == CONST0_RTX (GET_MODE (high))
|| low == CONST0_RTX (GET_MODE (low)))
? 2 : 4);
break;
}
default:
return false;
}
return true;
}
#include "gt-iq2000.h"