#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "tm.h"
#include "rtl.h"
#include "regs.h"
#include "hard-reg-set.h"
#include "basic-block.h"
#include "real.h"
#include "insn-config.h"
#include "conditions.h"
#include "insn-flags.h"
#include "insn-attr.h"
#include "insn-codes.h"
#include "recog.h"
#include "output.h"
#include "tree.h"
#include "expr.h"
#include "flags.h"
#include "reload.h"
#include "tm_p.h"
#include "function.h"
#include "toplev.h"
#include "optabs.h"
#include "libfuncs.h"
#include "ggc.h"
#include "target.h"
#include "target-def.h"
#include "langhooks.h"
#include "tree-gimple.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
};
rtx branch_cmp[2];
enum cmp_type branch_type;
char xtensa_hard_regno_mode_ok[(int) MAX_MACHINE_MODE][FIRST_PSEUDO_REGISTER];
unsigned xtensa_current_frame_size;
#define LARGEST_MOVE_RATIO 15
struct machine_function GTY(())
{
int accesses_prev_frame;
bool need_a7_copy;
bool vararg_a7;
rtx set_frame_ptr_insn;
};
const char xtensa_leaf_regs[FIRST_PSEUDO_REGISTER] =
{
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1
};
const enum reg_class xtensa_regno_to_class[FIRST_PSEUDO_REGISTER] =
{
RL_REGS, SP_REG, RL_REGS, RL_REGS,
RL_REGS, RL_REGS, RL_REGS, GR_REGS,
RL_REGS, RL_REGS, RL_REGS, RL_REGS,
RL_REGS, RL_REGS, RL_REGS, RL_REGS,
AR_REGS, AR_REGS, BR_REGS,
FP_REGS, FP_REGS, FP_REGS, FP_REGS,
FP_REGS, FP_REGS, FP_REGS, FP_REGS,
FP_REGS, FP_REGS, FP_REGS, FP_REGS,
FP_REGS, FP_REGS, FP_REGS, FP_REGS,
ACC_REG,
};
enum reg_class xtensa_char_to_class[256] =
{
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, 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,
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, 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,
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, 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,
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, 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,
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, 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,
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, 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,
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, 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,
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, 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,
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, 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,
NO_REGS, NO_REGS, NO_REGS, NO_REGS,
};
static enum internal_test map_test_to_internal_test (enum rtx_code);
static rtx gen_int_relational (enum rtx_code, rtx, rtx, int *);
static rtx gen_float_relational (enum rtx_code, rtx, rtx);
static rtx gen_conditional_move (rtx);
static rtx fixup_subreg_mem (rtx);
static struct machine_function * xtensa_init_machine_status (void);
static bool xtensa_return_in_msb (tree);
static void printx (FILE *, signed int);
static void xtensa_function_epilogue (FILE *, HOST_WIDE_INT);
static rtx xtensa_builtin_saveregs (void);
static unsigned int xtensa_multibss_section_type_flags (tree, const char *,
int) ATTRIBUTE_UNUSED;
static section *xtensa_select_rtx_section (enum machine_mode, rtx,
unsigned HOST_WIDE_INT);
static bool xtensa_rtx_costs (rtx, int, int, int *);
static tree xtensa_build_builtin_va_list (void);
static bool xtensa_return_in_memory (tree, tree);
static tree xtensa_gimplify_va_arg_expr (tree, tree, tree *, tree *);
static const int reg_nonleaf_alloc_order[FIRST_PSEUDO_REGISTER] =
REG_ALLOC_ORDER;
#undef TARGET_ASM_FUNCTION_EPILOGUE
#define TARGET_ASM_FUNCTION_EPILOGUE xtensa_function_epilogue
#undef TARGET_ASM_ALIGNED_SI_OP
#define TARGET_ASM_ALIGNED_SI_OP "\t.word\t"
#undef TARGET_ASM_SELECT_RTX_SECTION
#define TARGET_ASM_SELECT_RTX_SECTION xtensa_select_rtx_section
#undef TARGET_DEFAULT_TARGET_FLAGS
#define TARGET_DEFAULT_TARGET_FLAGS (TARGET_DEFAULT | MASK_FUSED_MADD)
#undef TARGET_RTX_COSTS
#define TARGET_RTX_COSTS xtensa_rtx_costs
#undef TARGET_ADDRESS_COST
#define TARGET_ADDRESS_COST hook_int_rtx_0
#undef TARGET_BUILD_BUILTIN_VA_LIST
#define TARGET_BUILD_BUILTIN_VA_LIST xtensa_build_builtin_va_list
#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 xtensa_return_in_memory
#undef TARGET_SPLIT_COMPLEX_ARG
#define TARGET_SPLIT_COMPLEX_ARG hook_bool_tree_true
#undef TARGET_MUST_PASS_IN_STACK
#define TARGET_MUST_PASS_IN_STACK must_pass_in_stack_var_size
#undef TARGET_EXPAND_BUILTIN_SAVEREGS
#define TARGET_EXPAND_BUILTIN_SAVEREGS xtensa_builtin_saveregs
#undef TARGET_GIMPLIFY_VA_ARG_EXPR
#define TARGET_GIMPLIFY_VA_ARG_EXPR xtensa_gimplify_va_arg_expr
#undef TARGET_RETURN_IN_MSB
#define TARGET_RETURN_IN_MSB xtensa_return_in_msb
struct gcc_target targetm = TARGET_INITIALIZER;
bool
xtensa_simm8 (HOST_WIDE_INT v)
{
return v >= -128 && v <= 127;
}
bool
xtensa_simm8x256 (HOST_WIDE_INT v)
{
return (v & 255) == 0 && (v >= -32768 && v <= 32512);
}
bool
xtensa_simm12b (HOST_WIDE_INT v)
{
return v >= -2048 && v <= 2047;
}
static bool
xtensa_uimm8 (HOST_WIDE_INT v)
{
return v >= 0 && v <= 255;
}
static bool
xtensa_uimm8x2 (HOST_WIDE_INT v)
{
return (v & 1) == 0 && (v >= 0 && v <= 510);
}
static bool
xtensa_uimm8x4 (HOST_WIDE_INT v)
{
return (v & 3) == 0 && (v >= 0 && v <= 1020);
}
static bool
xtensa_b4const (HOST_WIDE_INT v)
{
switch (v)
{
case -1:
case 1:
case 2:
case 3:
case 4:
case 5:
case 6:
case 7:
case 8:
case 10:
case 12:
case 16:
case 32:
case 64:
case 128:
case 256:
return true;
}
return false;
}
bool
xtensa_b4const_or_zero (HOST_WIDE_INT v)
{
if (v == 0)
return true;
return xtensa_b4const (v);
}
bool
xtensa_b4constu (HOST_WIDE_INT v)
{
switch (v)
{
case 32768:
case 65536:
case 2:
case 3:
case 4:
case 5:
case 6:
case 7:
case 8:
case 10:
case 12:
case 16:
case 32:
case 64:
case 128:
case 256:
return true;
}
return false;
}
bool
xtensa_mask_immediate (HOST_WIDE_INT v)
{
#define MAX_MASK_SIZE 16
int mask_size;
for (mask_size = 1; mask_size <= MAX_MASK_SIZE; mask_size++)
{
if ((v & 1) == 0)
return false;
v = v >> 1;
if (v == 0)
return true;
}
return false;
}
bool
xtensa_const_ok_for_letter_p (HOST_WIDE_INT v, int c)
{
switch (c)
{
case 'I': return xtensa_simm12b (v);
case 'J': return xtensa_simm8 (v);
case 'K': return (v == 0 || xtensa_b4const (v));
case 'L': return xtensa_b4constu (v);
case 'M': return (v >= -32 && v <= 95);
case 'N': return xtensa_simm8x256 (v);
case 'O': return (v == -1 || (v >= 1 && v <= 15));
case 'P': return xtensa_mask_immediate (v);
default: break;
}
return false;
}
int
xt_true_regnum (rtx x)
{
if (GET_CODE (x) == REG)
{
if (reg_renumber
&& REGNO (x) >= FIRST_PSEUDO_REGISTER
&& reg_renumber[REGNO (x)] >= 0)
return reg_renumber[REGNO (x)];
return REGNO (x);
}
if (GET_CODE (x) == SUBREG)
{
int base = xt_true_regnum (SUBREG_REG (x));
if (base >= 0 && base < FIRST_PSEUDO_REGISTER)
return base + subreg_regno_offset (REGNO (SUBREG_REG (x)),
GET_MODE (SUBREG_REG (x)),
SUBREG_BYTE (x), GET_MODE (x));
}
return -1;
}
int
xtensa_valid_move (enum machine_mode mode, rtx *operands)
{
if (register_operand (operands[0], mode))
{
int dst_regnum = xt_true_regnum (operands[0]);
if (dst_regnum == STACK_POINTER_REGNUM)
return (mode == SImode
&& register_operand (operands[1], mode)
&& !ACC_REG_P (xt_true_regnum (operands[1])));
if (!ACC_REG_P (dst_regnum))
return true;
}
if (register_operand (operands[1], mode))
{
int src_regnum = xt_true_regnum (operands[1]);
if (!ACC_REG_P (src_regnum))
return true;
}
return FALSE;
}
int
smalloffset_mem_p (rtx op)
{
if (GET_CODE (op) == MEM)
{
rtx addr = XEXP (op, 0);
if (GET_CODE (addr) == REG)
return REG_OK_FOR_BASE_P (addr);
if (GET_CODE (addr) == PLUS)
{
rtx offset = XEXP (addr, 0);
HOST_WIDE_INT val;
if (GET_CODE (offset) != CONST_INT)
offset = XEXP (addr, 1);
if (GET_CODE (offset) != CONST_INT)
return FALSE;
val = INTVAL (offset);
return (val & 3) == 0 && (val >= 0 && val <= 60);
}
}
return FALSE;
}
int
constantpool_address_p (rtx addr)
{
rtx sym = addr;
if (GET_CODE (addr) == CONST)
{
rtx offset;
addr = XEXP (addr, 0);
if (GET_CODE (addr) != PLUS)
return FALSE;
offset = XEXP (addr, 1);
if ((GET_CODE (offset) != CONST_INT)
|| ((INTVAL (offset) & 3) != 0))
return FALSE;
sym = XEXP (addr, 0);
}
if ((GET_CODE (sym) == SYMBOL_REF)
&& CONSTANT_POOL_ADDRESS_P (sym))
return TRUE;
return FALSE;
}
int
constantpool_mem_p (rtx op)
{
if (GET_CODE (op) == SUBREG)
op = SUBREG_REG (op);
if (GET_CODE (op) == MEM)
return constantpool_address_p (XEXP (op, 0));
return FALSE;
}
void
xtensa_extend_reg (rtx dst, rtx src)
{
rtx temp = gen_reg_rtx (SImode);
rtx shift = GEN_INT (BITS_PER_WORD - GET_MODE_BITSIZE (GET_MODE (src)));
src = simplify_gen_subreg (SImode, src, GET_MODE (src), 0);
dst = simplify_gen_subreg (SImode, dst, GET_MODE (dst), 0);
emit_insn (gen_ashlsi3 (temp, src, shift));
emit_insn (gen_ashrsi3 (dst, temp, shift));
}
bool
xtensa_mem_offset (unsigned v, enum machine_mode mode)
{
switch (mode)
{
case BLKmode:
return (xtensa_uimm8 (v)
&& xtensa_uimm8 (v + MOVE_MAX * LARGEST_MOVE_RATIO));
case QImode:
return xtensa_uimm8 (v);
case HImode:
return xtensa_uimm8x2 (v);
case DFmode:
return (xtensa_uimm8x4 (v) && xtensa_uimm8x4 (v + 4));
default:
break;
}
return xtensa_uimm8x4 (v);
}
bool
xtensa_extra_constraint (rtx op, int c)
{
if (GET_CODE (op) != MEM)
return (c >= 'R' && c <= 'U'
&& reload_in_progress && GET_CODE (op) == REG
&& REGNO (op) >= FIRST_PSEUDO_REGISTER);
switch (c)
{
case 'R': return smalloffset_mem_p (op);
case 'T': return !TARGET_CONST16 && constantpool_mem_p (op);
case 'U': return !constantpool_mem_p (op);
default: break;
}
return false;
}
static enum internal_test
map_test_to_internal_test (enum rtx_code test_code)
{
enum internal_test test = ITEST_MAX;
switch (test_code)
{
default: break;
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;
}
return test;
}
static rtx
gen_int_relational (enum rtx_code test_code,
rtx cmp0,
rtx cmp1,
int *p_invert )
{
struct cmp_info
{
enum rtx_code test_code;
bool (*const_range_p) (HOST_WIDE_INT);
int const_add;
int reverse_regs;
int invert_const;
int invert_reg;
int unsignedp;
};
static struct cmp_info info[ (int)ITEST_MAX ] = {
{ EQ, xtensa_b4const_or_zero, 0, 0, 0, 0, 0 },
{ NE, xtensa_b4const_or_zero, 0, 0, 0, 0, 0 },
{ LT, xtensa_b4const_or_zero, 1, 1, 1, 0, 0 },
{ GE, xtensa_b4const_or_zero, 0, 0, 0, 0, 0 },
{ LT, xtensa_b4const_or_zero, 0, 0, 0, 0, 0 },
{ GE, xtensa_b4const_or_zero, 1, 1, 1, 0, 0 },
{ LTU, xtensa_b4constu, 1, 1, 1, 0, 1 },
{ GEU, xtensa_b4constu, 0, 0, 0, 0, 1 },
{ LTU, xtensa_b4constu, 0, 0, 0, 0, 1 },
{ GEU, xtensa_b4constu, 1, 1, 1, 0, 1 },
};
enum internal_test test;
enum machine_mode mode;
struct cmp_info *p_info;
test = map_test_to_internal_test (test_code);
gcc_assert (test != ITEST_MAX);
p_info = &info[ (int)test ];
mode = GET_MODE (cmp0);
if (mode == VOIDmode)
mode = GET_MODE (cmp1);
if (GET_CODE (cmp1) == CONST_INT)
{
HOST_WIDE_INT value = INTVAL (cmp1);
unsigned HOST_WIDE_INT uvalue = (unsigned HOST_WIDE_INT)value;
if ((p_info->unsignedp ?
(uvalue + p_info->const_add > uvalue) :
(value + p_info->const_add > value)) != (p_info->const_add > 0))
{
cmp1 = force_reg (mode, cmp1);
}
else if (!(p_info->const_range_p) (value + p_info->const_add))
{
cmp1 = force_reg (mode, cmp1);
}
}
else if ((GET_CODE (cmp1) != REG) && (GET_CODE (cmp1) != SUBREG))
{
cmp1 = force_reg (mode, cmp1);
}
*p_invert = ((GET_CODE (cmp1) == CONST_INT)
? p_info->invert_const
: p_info->invert_reg);
if (GET_CODE (cmp1) == CONST_INT)
{
if (p_info->const_add != 0)
cmp1 = GEN_INT (INTVAL (cmp1) + p_info->const_add);
}
else if (p_info->reverse_regs)
{
rtx temp = cmp0;
cmp0 = cmp1;
cmp1 = temp;
}
return gen_rtx_fmt_ee (p_info->test_code, VOIDmode, cmp0, cmp1);
}
static rtx
gen_float_relational (enum rtx_code test_code,
rtx cmp0,
rtx cmp1 )
{
rtx (*gen_fn) (rtx, rtx, rtx);
rtx brtmp;
int reverse_regs, invert;
switch (test_code)
{
case EQ: reverse_regs = 0; invert = 0; gen_fn = gen_seq_sf; break;
case NE: reverse_regs = 0; invert = 1; gen_fn = gen_seq_sf; break;
case LE: reverse_regs = 0; invert = 0; gen_fn = gen_sle_sf; break;
case GT: reverse_regs = 1; invert = 0; gen_fn = gen_slt_sf; break;
case LT: reverse_regs = 0; invert = 0; gen_fn = gen_slt_sf; break;
case GE: reverse_regs = 1; invert = 0; gen_fn = gen_sle_sf; break;
default:
fatal_insn ("bad test", gen_rtx_fmt_ee (test_code, VOIDmode, cmp0, cmp1));
reverse_regs = 0; invert = 0; gen_fn = 0;
}
if (reverse_regs)
{
rtx temp = cmp0;
cmp0 = cmp1;
cmp1 = temp;
}
brtmp = gen_rtx_REG (CCmode, FPCC_REGNUM);
emit_insn (gen_fn (brtmp, cmp0, cmp1));
return gen_rtx_fmt_ee (invert ? EQ : NE, VOIDmode, brtmp, const0_rtx);
}
void
xtensa_expand_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];
rtx cmp;
int invert;
rtx label1, label2;
switch (type)
{
case CMP_DF:
default:
fatal_insn ("bad test", gen_rtx_fmt_ee (test_code, VOIDmode, cmp0, cmp1));
case CMP_SI:
invert = FALSE;
cmp = gen_int_relational (test_code, cmp0, cmp1, &invert);
break;
case CMP_SF:
if (!TARGET_HARD_FLOAT)
fatal_insn ("bad test", gen_rtx_fmt_ee (test_code, VOIDmode, cmp0, cmp1));
invert = FALSE;
cmp = gen_float_relational (test_code, cmp0, cmp1);
break;
}
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, cmp,
label1,
label2)));
}
static rtx
gen_conditional_move (rtx cmp)
{
enum rtx_code code = GET_CODE (cmp);
rtx op0 = branch_cmp[0];
rtx op1 = branch_cmp[1];
if (branch_type == CMP_SI)
{
if ((code == GT) && (op1 == constm1_rtx))
{
code = GE;
op1 = const0_rtx;
}
cmp = gen_rtx_fmt_ee (code, VOIDmode, cc0_rtx, const0_rtx);
if (boolean_operator (cmp, VOIDmode))
{
if (op0 == const0_rtx)
{
op0 = op1;
op1 = const0_rtx;
}
if (op1 != const0_rtx)
{
op0 = expand_binop (SImode, sub_optab, op0, op1,
0, 0, OPTAB_LIB_WIDEN);
op1 = const0_rtx;
}
}
else if (branch_operator (cmp, VOIDmode))
{
if (op0 == const0_rtx)
{
op0 = op1;
op1 = const0_rtx;
switch (code)
{
case LT: code = GE; break;
case GE: code = LT; break;
default: gcc_unreachable ();
}
}
if (op1 != const0_rtx)
return 0;
}
else
return 0;
return gen_rtx_fmt_ee (code, VOIDmode, op0, op1);
}
if (TARGET_HARD_FLOAT && (branch_type == CMP_SF))
return gen_float_relational (code, op0, op1);
return 0;
}
int
xtensa_expand_conditional_move (rtx *operands, int isflt)
{
rtx cmp;
rtx (*gen_fn) (rtx, rtx, rtx, rtx, rtx);
if (!(cmp = gen_conditional_move (operands[1])))
return 0;
if (isflt)
gen_fn = (branch_type == CMP_SI
? gen_movsfcc_internal0
: gen_movsfcc_internal1);
else
gen_fn = (branch_type == CMP_SI
? gen_movsicc_internal0
: gen_movsicc_internal1);
emit_insn (gen_fn (operands[0], XEXP (cmp, 0),
operands[2], operands[3], cmp));
return 1;
}
int
xtensa_expand_scc (rtx *operands)
{
rtx dest = operands[0];
rtx cmp = operands[1];
rtx one_tmp, zero_tmp;
rtx (*gen_fn) (rtx, rtx, rtx, rtx, rtx);
if (!(cmp = gen_conditional_move (cmp)))
return 0;
one_tmp = gen_reg_rtx (SImode);
zero_tmp = gen_reg_rtx (SImode);
emit_insn (gen_movsi (one_tmp, const_true_rtx));
emit_insn (gen_movsi (zero_tmp, const0_rtx));
gen_fn = (branch_type == CMP_SI
? gen_movsicc_internal0
: gen_movsicc_internal1);
emit_insn (gen_fn (dest, XEXP (cmp, 0), one_tmp, zero_tmp, cmp));
return 1;
}
void
xtensa_split_operand_pair (rtx operands[4], enum machine_mode mode)
{
switch (GET_CODE (operands[1]))
{
case REG:
operands[3] = gen_rtx_REG (mode, REGNO (operands[1]) + 1);
operands[2] = gen_rtx_REG (mode, REGNO (operands[1]));
break;
case MEM:
operands[3] = adjust_address (operands[1], mode, GET_MODE_SIZE (mode));
operands[2] = adjust_address (operands[1], mode, 0);
break;
case CONST_INT:
case CONST_DOUBLE:
split_double (operands[1], &operands[2], &operands[3]);
break;
default:
gcc_unreachable ();
}
switch (GET_CODE (operands[0]))
{
case REG:
operands[1] = gen_rtx_REG (mode, REGNO (operands[0]) + 1);
operands[0] = gen_rtx_REG (mode, REGNO (operands[0]));
break;
case MEM:
operands[1] = adjust_address (operands[0], mode, GET_MODE_SIZE (mode));
operands[0] = adjust_address (operands[0], mode, 0);
break;
default:
gcc_unreachable ();
}
}
int
xtensa_emit_move_sequence (rtx *operands, enum machine_mode mode)
{
if (CONSTANT_P (operands[1])
&& (GET_CODE (operands[1]) != CONST_INT
|| !xtensa_simm12b (INTVAL (operands[1]))))
{
if (!TARGET_CONST16)
operands[1] = force_const_mem (SImode, operands[1]);
if (mode != SImode)
{
if (register_operand (operands[0], mode))
{
operands[0] = simplify_gen_subreg (SImode, operands[0], mode, 0);
emit_move_insn (operands[0], operands[1]);
return 1;
}
else
{
operands[1] = force_reg (SImode, operands[1]);
operands[1] = gen_lowpart_SUBREG (mode, operands[1]);
}
}
}
if (!(reload_in_progress | reload_completed)
&& !xtensa_valid_move (mode, operands))
operands[1] = force_reg (mode, operands[1]);
operands[1] = xtensa_copy_incoming_a7 (operands[1]);
if (reload_in_progress)
{
operands[0] = fixup_subreg_mem (operands[0]);
operands[1] = fixup_subreg_mem (operands[1]);
}
return 0;
}
static rtx
fixup_subreg_mem (rtx x)
{
if (GET_CODE (x) == SUBREG
&& GET_CODE (SUBREG_REG (x)) == REG
&& REGNO (SUBREG_REG (x)) >= FIRST_PSEUDO_REGISTER)
{
rtx temp =
gen_rtx_SUBREG (GET_MODE (x),
reg_equiv_mem [REGNO (SUBREG_REG (x))],
SUBREG_BYTE (x));
x = alter_subreg (&temp);
}
return x;
}
rtx
xtensa_copy_incoming_a7 (rtx opnd)
{
rtx entry_insns = 0;
rtx reg, tmp;
enum machine_mode mode;
if (!cfun->machine->need_a7_copy)
return opnd;
gcc_assert (!cfun->machine->set_frame_ptr_insn);
mode = GET_MODE (opnd);
reg = opnd;
if (GET_CODE (reg) == SUBREG)
{
gcc_assert (SUBREG_BYTE (reg) == 0);
reg = SUBREG_REG (reg);
}
if (GET_CODE (reg) != REG
|| REGNO (reg) > A7_REG
|| REGNO (reg) + HARD_REGNO_NREGS (A7_REG, mode) <= A7_REG)
return opnd;
gcc_assert (REGNO (reg) + HARD_REGNO_NREGS (A7_REG, mode) - 1 == A7_REG);
cfun->machine->need_a7_copy = false;
push_to_sequence (entry_insns);
tmp = gen_reg_rtx (mode);
switch (mode)
{
case DFmode:
case DImode:
emit_insn (gen_movsi_internal (gen_rtx_SUBREG (SImode, tmp, 0),
gen_rtx_REG (SImode, A7_REG - 1)));
emit_insn (gen_movsi_internal (gen_rtx_SUBREG (SImode, tmp, 4),
gen_raw_REG (SImode, A7_REG)));
break;
case SFmode:
emit_insn (gen_movsf_internal (tmp, gen_raw_REG (mode, A7_REG)));
break;
case SImode:
emit_insn (gen_movsi_internal (tmp, gen_raw_REG (mode, A7_REG)));
break;
case HImode:
emit_insn (gen_movhi_internal (tmp, gen_raw_REG (mode, A7_REG)));
break;
case QImode:
emit_insn (gen_movqi_internal (tmp, gen_raw_REG (mode, A7_REG)));
break;
default:
gcc_unreachable ();
}
cfun->machine->set_frame_ptr_insn = emit_insn (gen_set_frame_ptr ());
entry_insns = get_insns ();
end_sequence ();
if (cfun->machine->vararg_a7)
{
emit_insn (entry_insns);
}
else
{
push_topmost_sequence ();
emit_insn_after (entry_insns, get_insns ());
pop_topmost_sequence ();
}
return tmp;
}
int
xtensa_expand_block_move (rtx *operands)
{
static const enum machine_mode mode_from_align[] =
{
VOIDmode, QImode, HImode, VOIDmode, SImode,
};
rtx dst_mem = operands[0];
rtx src_mem = operands[1];
HOST_WIDE_INT bytes, align;
int num_pieces, move_ratio;
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;
if (!optimize || (GET_CODE (operands[2]) != CONST_INT))
return 0;
bytes = INTVAL (operands[2]);
align = INTVAL (operands[3]);
if (bytes <= 0)
return 0;
if (align > MOVE_MAX)
align = MOVE_MAX;
move_ratio = 4;
if (optimize > 2)
move_ratio = LARGEST_MOVE_RATIO;
num_pieces = (bytes / align) + (bytes % align);
if (num_pieces > move_ratio)
return 0;
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 (bytes > 0)
{
int next_amount;
next_amount = (bytes >= 4 ? 4 : (bytes >= 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;
bytes -= 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]);
return 1;
}
void
xtensa_expand_nonlocal_goto (rtx *operands)
{
rtx goto_handler = operands[1];
rtx containing_fp = operands[3];
if (GET_CODE (containing_fp) != REG)
containing_fp = force_reg (Pmode, containing_fp);
goto_handler = replace_rtx (copy_rtx (goto_handler),
virtual_stack_vars_rtx,
containing_fp);
emit_library_call (gen_rtx_SYMBOL_REF (Pmode, "__xtensa_nonlocal_goto"),
0, VOIDmode, 2,
containing_fp, Pmode,
goto_handler, Pmode);
}
static struct machine_function *
xtensa_init_machine_status (void)
{
return ggc_alloc_cleared (sizeof (struct machine_function));
}
void
xtensa_setup_frame_addresses (void)
{
cfun->machine->accesses_prev_frame = 1;
emit_library_call
(gen_rtx_SYMBOL_REF (Pmode, "__xtensa_libgcc_window_spill"),
0, VOIDmode, 0);
}
void
xtensa_emit_loop_end (rtx insn, rtx *operands)
{
char done = 0;
for (insn = PREV_INSN (insn); insn && !done; insn = PREV_INSN (insn))
{
switch (GET_CODE (insn))
{
case NOTE:
case BARRIER:
break;
case CODE_LABEL:
output_asm_insn (TARGET_DENSITY ? "nop.n" : "nop", operands);
done = 1;
break;
default:
{
rtx body = PATTERN (insn);
if (GET_CODE (body) == JUMP_INSN)
{
output_asm_insn (TARGET_DENSITY ? "nop.n" : "nop", operands);
done = 1;
}
else if ((GET_CODE (body) != USE)
&& (GET_CODE (body) != CLOBBER))
done = 1;
}
break;
}
}
output_asm_insn ("# loop end for %0", operands);
}
char *
xtensa_emit_call (int callop, rtx *operands)
{
static char result[64];
rtx tgt = operands[callop];
if (GET_CODE (tgt) == CONST_INT)
sprintf (result, "call8\t0x%lx", INTVAL (tgt));
else if (register_operand (tgt, VOIDmode))
sprintf (result, "callx8\t%%%d", callop);
else
sprintf (result, "call8\t%%%d", callop);
return result;
}
int
xtensa_dbx_register_number (int regno)
{
int first = -1;
if (GP_REG_P (regno))
{
regno -= GP_REG_FIRST;
first = 0;
}
else if (BR_REG_P (regno))
{
regno -= BR_REG_FIRST;
first = 16;
}
else if (FP_REG_P (regno))
{
regno -= FP_REG_FIRST;
first = 48;
}
else if (ACC_REG_P (regno))
{
first = 0x200;
regno = 16;
}
if (first == -1)
return 0;
return first + regno;
}
void
init_cumulative_args (CUMULATIVE_ARGS *cum, int incoming)
{
cum->arg_words = 0;
cum->incoming = incoming;
}
void
function_arg_advance (CUMULATIVE_ARGS *cum, enum machine_mode mode, tree type)
{
int words, max;
int *arg_words;
arg_words = &cum->arg_words;
max = MAX_ARGS_IN_REGISTERS;
words = (((mode != BLKmode)
? (int) GET_MODE_SIZE (mode)
: int_size_in_bytes (type)) + UNITS_PER_WORD - 1) / UNITS_PER_WORD;
if (*arg_words < max
&& (targetm.calls.must_pass_in_stack (mode, type)
|| *arg_words + words > max))
*arg_words = max;
*arg_words += words;
}
rtx
function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode, tree type,
int incoming_p)
{
int regbase, words, max;
int *arg_words;
int regno;
arg_words = &cum->arg_words;
regbase = (incoming_p ? GP_ARG_FIRST : GP_OUTGOING_ARG_FIRST);
max = MAX_ARGS_IN_REGISTERS;
words = (((mode != BLKmode)
? (int) GET_MODE_SIZE (mode)
: int_size_in_bytes (type)) + UNITS_PER_WORD - 1) / UNITS_PER_WORD;
if (type && (TYPE_ALIGN (type) > BITS_PER_WORD))
{
int align = MIN (TYPE_ALIGN (type), STACK_BOUNDARY) / BITS_PER_WORD;
*arg_words = (*arg_words + align - 1) & -align;
}
if (*arg_words + words > max)
return (rtx)0;
regno = regbase + *arg_words;
if (cum->incoming && regno <= A7_REG && regno + words > A7_REG)
cfun->machine->need_a7_copy = true;
return gen_rtx_REG (mode, regno);
}
int
function_arg_boundary (enum machine_mode mode, tree type)
{
unsigned int alignment;
alignment = type ? TYPE_ALIGN (type) : GET_MODE_ALIGNMENT (mode);
if (alignment < PARM_BOUNDARY)
alignment = PARM_BOUNDARY;
if (alignment > STACK_BOUNDARY)
alignment = STACK_BOUNDARY;
return alignment;
}
static bool
xtensa_return_in_msb (tree valtype)
{
return (TARGET_BIG_ENDIAN
&& AGGREGATE_TYPE_P (valtype)
&& int_size_in_bytes (valtype) >= UNITS_PER_WORD);
}
void
override_options (void)
{
int regno;
enum machine_mode mode;
if (!TARGET_BOOLEANS && TARGET_HARD_FLOAT)
error ("boolean registers required for the floating-point option");
xtensa_char_to_class['q'] = SP_REG;
xtensa_char_to_class['a'] = GR_REGS;
xtensa_char_to_class['b'] = ((TARGET_BOOLEANS) ? BR_REGS : NO_REGS);
xtensa_char_to_class['f'] = ((TARGET_HARD_FLOAT) ? FP_REGS : NO_REGS);
xtensa_char_to_class['A'] = ((TARGET_MAC16) ? ACC_REG : NO_REGS);
xtensa_char_to_class['B'] = ((TARGET_SEXT) ? GR_REGS : NO_REGS);
xtensa_char_to_class['C'] = ((TARGET_MUL16) ? GR_REGS: NO_REGS);
xtensa_char_to_class['D'] = ((TARGET_DENSITY) ? GR_REGS: NO_REGS);
xtensa_char_to_class['d'] = ((TARGET_DENSITY) ? AR_REGS: NO_REGS);
xtensa_char_to_class['W'] = ((TARGET_CONST16) ? GR_REGS: NO_REGS);
for (mode = VOIDmode;
mode != MAX_MACHINE_MODE;
mode = (enum machine_mode) ((int) mode + 1))
{
int size = GET_MODE_SIZE (mode);
enum mode_class class = GET_MODE_CLASS (mode);
for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
{
int temp;
if (ACC_REG_P (regno))
temp = (TARGET_MAC16
&& (class == MODE_INT) && (size <= UNITS_PER_WORD));
else if (GP_REG_P (regno))
temp = ((regno & 1) == 0 || (size <= UNITS_PER_WORD));
else if (FP_REG_P (regno))
temp = (TARGET_HARD_FLOAT && (mode == SFmode));
else if (BR_REG_P (regno))
temp = (TARGET_BOOLEANS && (mode == CCmode));
else
temp = FALSE;
xtensa_hard_regno_mode_ok[(int) mode][regno] = temp;
}
}
init_machine_status = xtensa_init_machine_status;
if (flag_pic && TARGET_CONST16)
error ("-f%s is not supported with CONST16 instructions",
(flag_pic > 1 ? "PIC" : "pic"));
else if (XTENSA_ALWAYS_PIC)
{
if (TARGET_CONST16)
error ("PIC is required but not supported with CONST16 instructions");
flag_pic = 1;
}
if (flag_pic > 1)
flag_pic = 1;
if (flag_reorder_blocks_and_partition)
{
flag_reorder_blocks_and_partition = 0;
flag_reorder_blocks = 1;
}
}
static void
printx (FILE *file, signed int val)
{
if ((val > -0xa) && (val < 0xa))
fprintf (file, "%d", val);
else if (val < 0)
fprintf (file, "-0x%x", -val);
else
fprintf (file, "0x%x", val);
}
void
print_operand (FILE *file, rtx x, int letter)
{
if (!x)
error ("PRINT_OPERAND null pointer");
switch (letter)
{
case 'D':
if (GET_CODE (x) == REG || GET_CODE (x) == SUBREG)
fprintf (file, "%s", reg_names[xt_true_regnum (x) + 1]);
else
output_operand_lossage ("invalid %%D value");
break;
case 'v':
if (GET_CODE (x) == MEM)
{
if (MEM_VOLATILE_P (x))
fprintf (file, "memw\n\t");
}
else
output_operand_lossage ("invalid %%v value");
break;
case 'N':
if (GET_CODE (x) == MEM
&& (GET_MODE (x) == DFmode || GET_MODE (x) == DImode))
{
x = adjust_address (x, GET_MODE (x) == DFmode ? SFmode : SImode, 4);
output_address (XEXP (x, 0));
}
else
output_operand_lossage ("invalid %%N value");
break;
case 'K':
if (GET_CODE (x) == CONST_INT)
{
int num_bits = 0;
unsigned val = INTVAL (x);
while (val & 1)
{
num_bits += 1;
val = val >> 1;
}
if ((val != 0) || (num_bits == 0) || (num_bits > 16))
fatal_insn ("invalid mask", x);
fprintf (file, "%d", num_bits);
}
else
output_operand_lossage ("invalid %%K value");
break;
case 'L':
if (GET_CODE (x) == CONST_INT)
fprintf (file, "%ld", (32 - INTVAL (x)) & 0x1f);
else
output_operand_lossage ("invalid %%L value");
break;
case 'R':
if (GET_CODE (x) == CONST_INT)
fprintf (file, "%ld", INTVAL (x) & 0x1f);
else
output_operand_lossage ("invalid %%R value");
break;
case 'x':
if (GET_CODE (x) == CONST_INT)
printx (file, INTVAL (x));
else
output_operand_lossage ("invalid %%x value");
break;
case 'd':
if (GET_CODE (x) == CONST_INT)
fprintf (file, "%ld", INTVAL (x));
else
output_operand_lossage ("invalid %%d value");
break;
case 't':
case 'b':
if (GET_CODE (x) == CONST_INT)
{
printx (file, INTVAL (x));
fputs (letter == 't' ? "@h" : "@l", file);
}
else if (GET_CODE (x) == CONST_DOUBLE)
{
REAL_VALUE_TYPE r;
REAL_VALUE_FROM_CONST_DOUBLE (r, x);
if (GET_MODE (x) == SFmode)
{
long l;
REAL_VALUE_TO_TARGET_SINGLE (r, l);
fprintf (file, "0x%08lx@%c", l, letter == 't' ? 'h' : 'l');
}
else
output_operand_lossage ("invalid %%t/%%b value");
}
else if (GET_CODE (x) == CONST)
{
if (GET_CODE (XEXP (x, 0)) != PLUS
|| (GET_CODE (XEXP (XEXP (x, 0), 0)) != SYMBOL_REF
&& GET_CODE (XEXP (XEXP (x, 0), 0)) != LABEL_REF)
|| GET_CODE (XEXP (XEXP (x, 0), 1)) != CONST_INT)
output_operand_lossage ("invalid %%t/%%b value");
print_operand (file, XEXP (XEXP (x, 0), 0), 0);
fputs (letter == 't' ? "@h" : "@l", file);
if (INTVAL (XEXP (XEXP (x, 0), 1)) >= 0)
fputs ("+", file);
print_operand (file, XEXP (XEXP (x, 0), 1), 0);
}
else
{
output_addr_const (file, x);
fputs (letter == 't' ? "@h" : "@l", file);
}
break;
default:
if (GET_CODE (x) == REG || GET_CODE (x) == SUBREG)
fprintf (file, "%s", reg_names[xt_true_regnum (x)]);
else if (GET_CODE (x) == MEM)
output_address (XEXP (x, 0));
else if (GET_CODE (x) == CONST_INT)
fprintf (file, "%ld", INTVAL (x));
else
output_addr_const (file, x);
}
}
void
print_operand_address (FILE *file, rtx addr)
{
if (!addr)
error ("PRINT_OPERAND_ADDRESS, null pointer");
switch (GET_CODE (addr))
{
default:
fatal_insn ("invalid address", addr);
break;
case REG:
fprintf (file, "%s, 0", reg_names [REGNO (addr)]);
break;
case PLUS:
{
rtx reg = (rtx)0;
rtx offset = (rtx)0;
rtx arg0 = XEXP (addr, 0);
rtx arg1 = XEXP (addr, 1);
if (GET_CODE (arg0) == REG)
{
reg = arg0;
offset = arg1;
}
else if (GET_CODE (arg1) == REG)
{
reg = arg1;
offset = arg0;
}
else
fatal_insn ("no register in address", addr);
if (CONSTANT_P (offset))
{
fprintf (file, "%s, ", reg_names [REGNO (reg)]);
output_addr_const (file, offset);
}
else
fatal_insn ("address offset not a constant", addr);
}
break;
case LABEL_REF:
case SYMBOL_REF:
case CONST_INT:
case CONST:
output_addr_const (file, addr);
break;
}
}
void
xtensa_output_literal (FILE *file, rtx x, enum machine_mode mode, int labelno)
{
long value_long[2];
REAL_VALUE_TYPE r;
int size;
fprintf (file, "\t.literal .LC%u, ", (unsigned) labelno);
switch (GET_MODE_CLASS (mode))
{
case MODE_FLOAT:
gcc_assert (GET_CODE (x) == CONST_DOUBLE);
REAL_VALUE_FROM_CONST_DOUBLE (r, x);
switch (mode)
{
case SFmode:
REAL_VALUE_TO_TARGET_SINGLE (r, value_long[0]);
fprintf (file, "0x%08lx\n", value_long[0]);
break;
case DFmode:
REAL_VALUE_TO_TARGET_DOUBLE (r, value_long);
fprintf (file, "0x%08lx, 0x%08lx\n",
value_long[0], value_long[1]);
break;
default:
gcc_unreachable ();
}
break;
case MODE_INT:
case MODE_PARTIAL_INT:
size = GET_MODE_SIZE (mode);
switch (size)
{
case 4:
output_addr_const (file, x);
fputs ("\n", file);
break;
case 8:
output_addr_const (file, operand_subword (x, 0, 0, DImode));
fputs (", ", file);
output_addr_const (file, operand_subword (x, 1, 0, DImode));
fputs ("\n", file);
break;
default:
gcc_unreachable ();
}
break;
default:
gcc_unreachable ();
}
}
#define STACK_BYTES (STACK_BOUNDARY / BITS_PER_UNIT)
#define XTENSA_STACK_ALIGN(LOC) (((LOC) + STACK_BYTES-1) & ~(STACK_BYTES-1))
long
compute_frame_size (int size)
{
if (cfun->static_chain_decl != NULL)
size += (1 * UNITS_PER_WORD);
xtensa_current_frame_size =
XTENSA_STACK_ALIGN (size
+ current_function_outgoing_args_size
+ (WINDOW_SIZE * UNITS_PER_WORD));
return xtensa_current_frame_size;
}
int
xtensa_frame_pointer_required (void)
{
if (cfun->machine->accesses_prev_frame)
return 1;
return 0;
}
void
xtensa_expand_prologue (void)
{
HOST_WIDE_INT total_size;
rtx size_rtx;
total_size = compute_frame_size (get_frame_size ());
size_rtx = GEN_INT (total_size);
if (total_size < (1 << (12+3)))
emit_insn (gen_entry (size_rtx, size_rtx));
else
{
rtx tmp_reg = gen_rtx_REG (Pmode, A8_REG);
emit_insn (gen_entry (size_rtx, GEN_INT (MIN_FRAME_SIZE)));
emit_move_insn (tmp_reg, GEN_INT (total_size - MIN_FRAME_SIZE));
emit_insn (gen_subsi3 (tmp_reg, stack_pointer_rtx, tmp_reg));
emit_move_insn (stack_pointer_rtx, tmp_reg);
}
if (frame_pointer_needed)
{
if (cfun->machine->set_frame_ptr_insn)
{
rtx first, insn;
push_topmost_sequence ();
first = get_insns ();
pop_topmost_sequence ();
for (insn = first;
insn != cfun->machine->set_frame_ptr_insn;
insn = NEXT_INSN (insn))
{
if (INSN_P (insn))
PATTERN (insn) = replace_rtx (copy_rtx (PATTERN (insn)),
hard_frame_pointer_rtx,
stack_pointer_rtx);
}
}
else
emit_move_insn (hard_frame_pointer_rtx, stack_pointer_rtx);
}
}
void
xtensa_function_epilogue (FILE *file ATTRIBUTE_UNUSED,
HOST_WIDE_INT size ATTRIBUTE_UNUSED)
{
xtensa_current_frame_size = 0;
}
rtx
xtensa_return_addr (int count, rtx frame)
{
rtx result, retaddr;
if (count == -1)
retaddr = gen_rtx_REG (Pmode, A0_REG);
else
{
rtx addr = plus_constant (frame, -4 * UNITS_PER_WORD);
addr = memory_address (Pmode, addr);
retaddr = gen_reg_rtx (Pmode);
emit_move_insn (retaddr, gen_rtx_MEM (Pmode, addr));
}
result = gen_reg_rtx (Pmode);
emit_insn (gen_fix_return_addr (result, retaddr));
return result;
}
static tree
xtensa_build_builtin_va_list (void)
{
tree f_stk, f_reg, f_ndx, record, type_decl;
record = (*lang_hooks.types.make_type) (RECORD_TYPE);
type_decl = build_decl (TYPE_DECL, get_identifier ("__va_list_tag"), record);
f_stk = build_decl (FIELD_DECL, get_identifier ("__va_stk"),
ptr_type_node);
f_reg = build_decl (FIELD_DECL, get_identifier ("__va_reg"),
ptr_type_node);
f_ndx = build_decl (FIELD_DECL, get_identifier ("__va_ndx"),
integer_type_node);
DECL_FIELD_CONTEXT (f_stk) = record;
DECL_FIELD_CONTEXT (f_reg) = record;
DECL_FIELD_CONTEXT (f_ndx) = record;
TREE_CHAIN (record) = type_decl;
TYPE_NAME (record) = type_decl;
TYPE_FIELDS (record) = f_stk;
TREE_CHAIN (f_stk) = f_reg;
TREE_CHAIN (f_reg) = f_ndx;
layout_type (record);
return record;
}
static rtx
xtensa_builtin_saveregs (void)
{
rtx gp_regs, dest;
int arg_words = current_function_args_info.arg_words;
int gp_left = MAX_ARGS_IN_REGISTERS - arg_words;
if (gp_left <= 0)
return const0_rtx;
gp_regs = assign_stack_local
(BLKmode, MAX_ARGS_IN_REGISTERS * UNITS_PER_WORD, -1);
set_mem_alias_set (gp_regs, get_varargs_alias_set ());
dest = change_address (gp_regs, SImode,
plus_constant (XEXP (gp_regs, 0),
arg_words * UNITS_PER_WORD));
cfun->machine->need_a7_copy = true;
cfun->machine->vararg_a7 = true;
move_block_from_reg (GP_ARG_FIRST + arg_words, dest, gp_left);
return XEXP (gp_regs, 0);
}
void
xtensa_va_start (tree valist, rtx nextarg ATTRIBUTE_UNUSED)
{
tree f_stk, stk;
tree f_reg, reg;
tree f_ndx, ndx;
tree t, u;
int arg_words;
arg_words = current_function_args_info.arg_words;
f_stk = TYPE_FIELDS (va_list_type_node);
f_reg = TREE_CHAIN (f_stk);
f_ndx = TREE_CHAIN (f_reg);
stk = build3 (COMPONENT_REF, TREE_TYPE (f_stk), valist, f_stk, NULL_TREE);
reg = build3 (COMPONENT_REF, TREE_TYPE (f_reg), valist, f_reg, NULL_TREE);
ndx = build3 (COMPONENT_REF, TREE_TYPE (f_ndx), valist, f_ndx, NULL_TREE);
u = make_tree (ptr_type_node, expand_builtin_saveregs ());
t = build2 (MODIFY_EXPR, ptr_type_node, reg, u);
TREE_SIDE_EFFECTS (t) = 1;
expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
u = make_tree (ptr_type_node, virtual_incoming_args_rtx);
u = fold_build2 (PLUS_EXPR, ptr_type_node, u,
build_int_cst (NULL_TREE, -32));
t = build2 (MODIFY_EXPR, ptr_type_node, stk, u);
TREE_SIDE_EFFECTS (t) = 1;
expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
if (arg_words >= MAX_ARGS_IN_REGISTERS)
arg_words += 2;
u = build_int_cst (NULL_TREE, arg_words * UNITS_PER_WORD);
t = build2 (MODIFY_EXPR, integer_type_node, ndx, u);
TREE_SIDE_EFFECTS (t) = 1;
expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
}
static tree
xtensa_gimplify_va_arg_expr (tree valist, tree type, tree *pre_p,
tree *post_p ATTRIBUTE_UNUSED)
{
tree f_stk, stk;
tree f_reg, reg;
tree f_ndx, ndx;
tree type_size, array, orig_ndx, addr, size, va_size, t;
tree lab_false, lab_over, lab_false2;
bool indirect;
indirect = pass_by_reference (NULL, TYPE_MODE (type), type, false);
if (indirect)
type = build_pointer_type (type);
if (TREE_CODE (type) == COMPLEX_TYPE)
{
tree real_part, imag_part;
real_part = xtensa_gimplify_va_arg_expr (valist, TREE_TYPE (type),
pre_p, NULL);
real_part = get_initialized_tmp_var (real_part, pre_p, NULL);
imag_part = xtensa_gimplify_va_arg_expr (valist, TREE_TYPE (type),
pre_p, NULL);
imag_part = get_initialized_tmp_var (imag_part, pre_p, NULL);
return build2 (COMPLEX_EXPR, type, real_part, imag_part);
}
f_stk = TYPE_FIELDS (va_list_type_node);
f_reg = TREE_CHAIN (f_stk);
f_ndx = TREE_CHAIN (f_reg);
stk = build3 (COMPONENT_REF, TREE_TYPE (f_stk), valist, f_stk, NULL_TREE);
reg = build3 (COMPONENT_REF, TREE_TYPE (f_reg), valist, f_reg, NULL_TREE);
ndx = build3 (COMPONENT_REF, TREE_TYPE (f_ndx), valist, f_ndx, NULL_TREE);
type_size = size_in_bytes (type);
va_size = round_up (type_size, UNITS_PER_WORD);
gimplify_expr (&va_size, pre_p, NULL, is_gimple_val, fb_rvalue);
orig_ndx = get_initialized_tmp_var (ndx, pre_p, NULL);
if (TYPE_ALIGN (type) > BITS_PER_WORD)
{
int align = MIN (TYPE_ALIGN (type), STACK_BOUNDARY) / BITS_PER_UNIT;
t = build2 (PLUS_EXPR, integer_type_node, orig_ndx,
build_int_cst (NULL_TREE, align - 1));
t = build2 (BIT_AND_EXPR, integer_type_node, t,
build_int_cst (NULL_TREE, -align));
t = build2 (MODIFY_EXPR, integer_type_node, orig_ndx, t);
gimplify_and_add (t, pre_p);
}
t = fold_convert (integer_type_node, va_size);
t = build2 (PLUS_EXPR, integer_type_node, orig_ndx, t);
t = build2 (MODIFY_EXPR, integer_type_node, ndx, t);
gimplify_and_add (t, pre_p);
array = create_tmp_var (ptr_type_node, NULL);
lab_over = NULL;
if (!targetm.calls.must_pass_in_stack (TYPE_MODE (type), type))
{
lab_false = create_artificial_label ();
lab_over = create_artificial_label ();
t = build_int_cst (NULL_TREE, MAX_ARGS_IN_REGISTERS * UNITS_PER_WORD);
t = build2 (GT_EXPR, boolean_type_node, ndx, t);
t = build3 (COND_EXPR, void_type_node, t,
build1 (GOTO_EXPR, void_type_node, lab_false),
NULL_TREE);
gimplify_and_add (t, pre_p);
t = build2 (MODIFY_EXPR, void_type_node, array, reg);
gimplify_and_add (t, pre_p);
t = build1 (GOTO_EXPR, void_type_node, lab_over);
gimplify_and_add (t, pre_p);
t = build1 (LABEL_EXPR, void_type_node, lab_false);
gimplify_and_add (t, pre_p);
}
lab_false2 = create_artificial_label ();
t = build_int_cst (NULL_TREE, MAX_ARGS_IN_REGISTERS * UNITS_PER_WORD);
t = build2 (GT_EXPR, boolean_type_node, orig_ndx, t);
t = build3 (COND_EXPR, void_type_node, t,
build1 (GOTO_EXPR, void_type_node, lab_false2),
NULL_TREE);
gimplify_and_add (t, pre_p);
t = size_binop (PLUS_EXPR, va_size, size_int (32));
t = fold_convert (integer_type_node, t);
t = build2 (MODIFY_EXPR, integer_type_node, ndx, t);
gimplify_and_add (t, pre_p);
t = build1 (LABEL_EXPR, void_type_node, lab_false2);
gimplify_and_add (t, pre_p);
t = build2 (MODIFY_EXPR, void_type_node, array, stk);
gimplify_and_add (t, pre_p);
if (lab_over)
{
t = build1 (LABEL_EXPR, void_type_node, lab_over);
gimplify_and_add (t, pre_p);
}
if (BYTES_BIG_ENDIAN && TREE_CODE (type_size) == INTEGER_CST)
{
t = size_int (PARM_BOUNDARY / BITS_PER_UNIT);
t = fold_build2 (GE_EXPR, boolean_type_node, type_size, t);
t = fold_build3 (COND_EXPR, sizetype, t, va_size, type_size);
size = t;
}
else
size = va_size;
t = fold_convert (ptr_type_node, ndx);
addr = build2 (PLUS_EXPR, ptr_type_node, array, t);
t = fold_convert (ptr_type_node, size);
addr = build2 (MINUS_EXPR, ptr_type_node, addr, t);
addr = fold_convert (build_pointer_type (type), addr);
if (indirect)
addr = build_va_arg_indirect_ref (addr);
return build_va_arg_indirect_ref (addr);
}
enum reg_class
xtensa_preferred_reload_class (rtx x, enum reg_class class, int isoutput)
{
if (!isoutput && CONSTANT_P (x) && GET_CODE (x) == CONST_DOUBLE)
return NO_REGS;
if (class == AR_REGS || class == GR_REGS)
return RL_REGS;
return class;
}
enum reg_class
xtensa_secondary_reload_class (enum reg_class class,
enum machine_mode mode ATTRIBUTE_UNUSED,
rtx x, int isoutput)
{
int regno;
if (GET_CODE (x) == SIGN_EXTEND)
x = XEXP (x, 0);
regno = xt_true_regnum (x);
if (!isoutput)
{
if (class == FP_REGS && constantpool_mem_p (x))
return RL_REGS;
}
if (ACC_REG_P (regno))
return ((class == GR_REGS || class == RL_REGS) ? NO_REGS : RL_REGS);
if (class == ACC_REG)
return (GP_REG_P (regno) ? NO_REGS : RL_REGS);
return NO_REGS;
}
void
order_regs_for_local_alloc (void)
{
if (!leaf_function_p ())
{
memcpy (reg_alloc_order, reg_nonleaf_alloc_order,
FIRST_PSEUDO_REGISTER * sizeof (int));
}
else
{
int i, num_arg_regs;
int nxt = 0;
num_arg_regs = current_function_args_info.arg_words;
if (num_arg_regs > MAX_ARGS_IN_REGISTERS)
num_arg_regs = MAX_ARGS_IN_REGISTERS;
for (i = GP_ARG_FIRST; i < 16 - num_arg_regs; i++)
reg_alloc_order[nxt++] = i + num_arg_regs;
for (i = 0; i < num_arg_regs; i++)
reg_alloc_order[nxt++] = GP_ARG_FIRST + i;
for (i = 0; i < BR_REG_NUM; i++)
reg_alloc_order[nxt++] = BR_REG_FIRST + i;
for (i = 0; i < 16; i++)
reg_alloc_order[nxt++] = FP_REG_FIRST + i;
reg_alloc_order[nxt++] = 0;
reg_alloc_order[nxt++] = 1;
reg_alloc_order[nxt++] = 16;
reg_alloc_order[nxt++] = 17;
reg_alloc_order[nxt++] = ACC_REG_FIRST;
}
}
static unsigned int
xtensa_multibss_section_type_flags (tree decl, const char *name, int reloc)
{
unsigned int flags = default_section_type_flags (decl, name, reloc);
const char *suffix;
suffix = strrchr (name, '.');
if (suffix && strcmp (suffix, ".bss") == 0)
{
if (!decl || (TREE_CODE (decl) == VAR_DECL
&& DECL_INITIAL (decl) == NULL_TREE))
flags |= SECTION_BSS;
else
warning (0, "only uninitialized variables can be placed in a "
".bss section");
}
return flags;
}
static section *
xtensa_select_rtx_section (enum machine_mode mode ATTRIBUTE_UNUSED,
rtx x ATTRIBUTE_UNUSED,
unsigned HOST_WIDE_INT align ATTRIBUTE_UNUSED)
{
return function_section (current_function_decl);
}
static bool
xtensa_rtx_costs (rtx x, int code, int outer_code, int *total)
{
switch (code)
{
case CONST_INT:
switch (outer_code)
{
case SET:
if (xtensa_simm12b (INTVAL (x)))
{
*total = 4;
return true;
}
break;
case PLUS:
if (xtensa_simm8 (INTVAL (x))
|| xtensa_simm8x256 (INTVAL (x)))
{
*total = 0;
return true;
}
break;
case AND:
if (xtensa_mask_immediate (INTVAL (x)))
{
*total = 0;
return true;
}
break;
case COMPARE:
if ((INTVAL (x) == 0) || xtensa_b4const (INTVAL (x)))
{
*total = 0;
return true;
}
break;
case ASHIFT:
case ASHIFTRT:
case LSHIFTRT:
case ROTATE:
case ROTATERT:
default: break;
}
if (xtensa_simm12b (INTVAL (x)))
*total = 5;
else if (TARGET_CONST16)
*total = COSTS_N_INSNS (2);
else
*total = 6;
return true;
case CONST:
case LABEL_REF:
case SYMBOL_REF:
if (TARGET_CONST16)
*total = COSTS_N_INSNS (2);
else
*total = 5;
return true;
case CONST_DOUBLE:
if (TARGET_CONST16)
*total = COSTS_N_INSNS (4);
else
*total = 7;
return true;
case MEM:
{
int num_words =
(GET_MODE_SIZE (GET_MODE (x)) > UNITS_PER_WORD) ? 2 : 1;
if (memory_address_p (GET_MODE (x), XEXP ((x), 0)))
*total = COSTS_N_INSNS (num_words);
else
*total = COSTS_N_INSNS (2*num_words);
return true;
}
case FFS:
*total = COSTS_N_INSNS (TARGET_NSA ? 5 : 50);
return true;
case NOT:
*total = COSTS_N_INSNS ((GET_MODE (x) == DImode) ? 3 : 2);
return true;
case AND:
case IOR:
case XOR:
if (GET_MODE (x) == DImode)
*total = COSTS_N_INSNS (2);
else
*total = COSTS_N_INSNS (1);
return true;
case ASHIFT:
case ASHIFTRT:
case LSHIFTRT:
if (GET_MODE (x) == DImode)
*total = COSTS_N_INSNS (50);
else
*total = COSTS_N_INSNS (1);
return true;
case ABS:
{
enum machine_mode xmode = GET_MODE (x);
if (xmode == SFmode)
*total = COSTS_N_INSNS (TARGET_HARD_FLOAT ? 1 : 50);
else if (xmode == DFmode)
*total = COSTS_N_INSNS (50);
else
*total = COSTS_N_INSNS (4);
return true;
}
case PLUS:
case MINUS:
{
enum machine_mode xmode = GET_MODE (x);
if (xmode == SFmode)
*total = COSTS_N_INSNS (TARGET_HARD_FLOAT ? 1 : 50);
else if (xmode == DFmode || xmode == DImode)
*total = COSTS_N_INSNS (50);
else
*total = COSTS_N_INSNS (1);
return true;
}
case NEG:
*total = COSTS_N_INSNS ((GET_MODE (x) == DImode) ? 4 : 2);
return true;
case MULT:
{
enum machine_mode xmode = GET_MODE (x);
if (xmode == SFmode)
*total = COSTS_N_INSNS (TARGET_HARD_FLOAT ? 4 : 50);
else if (xmode == DFmode || xmode == DImode)
*total = COSTS_N_INSNS (50);
else if (TARGET_MUL32)
*total = COSTS_N_INSNS (4);
else if (TARGET_MAC16)
*total = COSTS_N_INSNS (16);
else if (TARGET_MUL16)
*total = COSTS_N_INSNS (12);
else
*total = COSTS_N_INSNS (50);
return true;
}
case DIV:
case MOD:
{
enum machine_mode xmode = GET_MODE (x);
if (xmode == SFmode)
{
*total = COSTS_N_INSNS (TARGET_HARD_FLOAT_DIV ? 8 : 50);
return true;
}
else if (xmode == DFmode)
{
*total = COSTS_N_INSNS (50);
return true;
}
}
case UDIV:
case UMOD:
{
enum machine_mode xmode = GET_MODE (x);
if (xmode == DImode)
*total = COSTS_N_INSNS (50);
else if (TARGET_DIV32)
*total = COSTS_N_INSNS (32);
else
*total = COSTS_N_INSNS (50);
return true;
}
case SQRT:
if (GET_MODE (x) == SFmode)
*total = COSTS_N_INSNS (TARGET_HARD_FLOAT_SQRT ? 8 : 50);
else
*total = COSTS_N_INSNS (50);
return true;
case SMIN:
case UMIN:
case SMAX:
case UMAX:
*total = COSTS_N_INSNS (TARGET_MINMAX ? 1 : 50);
return true;
case SIGN_EXTRACT:
case SIGN_EXTEND:
*total = COSTS_N_INSNS (TARGET_SEXT ? 1 : 2);
return true;
case ZERO_EXTRACT:
case ZERO_EXTEND:
*total = COSTS_N_INSNS (1);
return true;
default:
return false;
}
}
static bool
xtensa_return_in_memory (tree type, tree fntype ATTRIBUTE_UNUSED)
{
return ((unsigned HOST_WIDE_INT) int_size_in_bytes (type)
> 4 * UNITS_PER_WORD);
}
#include "gt-xtensa.h"