#include <stdio.h>
#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "tm.h"
#include "rtl.h"
#include "tree.h"
#include "tm_p.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 "recog.h"
#include "expr.h"
#include "libfuncs.h"
#include "toplev.h"
#include "basic-block.h"
#include "function.h"
#include "ggc.h"
#include "reload.h"
#include "target.h"
#include "target-def.h"
static void emit_move_after_reload (rtx, rtx, rtx);
static rtx simplify_logical (enum machine_mode, int, rtx, rtx *);
static void m68hc11_emit_logical (enum machine_mode, int, rtx *);
static void m68hc11_reorg (void);
static int go_if_legitimate_address_internal (rtx, enum machine_mode, int);
static int register_indirect_p (rtx, enum machine_mode, int);
static rtx m68hc11_expand_compare (enum rtx_code, rtx, rtx);
static int must_parenthesize (rtx);
static int m68hc11_address_cost (rtx);
static int m68hc11_shift_cost (enum machine_mode, rtx, int);
static int m68hc11_rtx_costs_1 (rtx, enum rtx_code, enum rtx_code);
static bool m68hc11_rtx_costs (rtx, int, int, int *);
static int m68hc11_auto_inc_p (rtx);
static tree m68hc11_handle_fntype_attribute (tree *, tree, tree, int, bool *);
const struct attribute_spec m68hc11_attribute_table[];
void create_regs_rtx (void);
static void asm_print_register (FILE *, int);
static void m68hc11_output_function_epilogue (FILE *, HOST_WIDE_INT);
static void m68hc11_asm_out_constructor (rtx, int);
static void m68hc11_asm_out_destructor (rtx, int);
static void m68hc11_file_start (void);
static void m68hc11_encode_section_info (tree, rtx, int);
static const char *m68hc11_strip_name_encoding (const char* str);
static unsigned int m68hc11_section_type_flags (tree, const char*, int);
static int autoinc_mode (rtx);
static int m68hc11_make_autoinc_notes (rtx *, void *);
static void m68hc11_init_libfuncs (void);
static rtx m68hc11_struct_value_rtx (tree, int);
static bool m68hc11_return_in_memory (tree, tree);
int debug_m6811 = 0;
extern FILE *asm_out_file;
rtx ix_reg;
rtx iy_reg;
rtx d_reg;
rtx m68hc11_soft_tmp_reg;
static GTY(()) rtx stack_push_word;
static GTY(()) rtx stack_pop_word;
static GTY(()) rtx z_reg;
static GTY(()) rtx z_reg_qi;
static int regs_inited = 0;
int current_function_interrupt;
int current_function_trap;
int current_function_far;
HOST_WIDE_INT m68hc11_min_offset = 0;
HOST_WIDE_INT m68hc11_max_offset = 256;
enum reg_class m68hc11_base_reg_class = A_REGS;
enum reg_class m68hc11_index_reg_class = NO_REGS;
enum reg_class m68hc11_tmp_regs_class = NO_REGS;
unsigned char m68hc11_reg_valid_for_base[FIRST_PSEUDO_REGISTER];
unsigned char m68hc11_reg_valid_for_index[FIRST_PSEUDO_REGISTER];
int m68hc11_sp_correction;
#define ADDR_STRICT 0x01
#define ADDR_INCDEC 0x02
#define ADDR_INDEXED 0x04
#define ADDR_OFFSET 0x08
#define ADDR_INDIRECT 0x10
#define ADDR_CONST 0x20
int m68hc11_addr_mode;
int m68hc11_mov_addr_mode;
rtx m68hc11_compare_op0;
rtx m68hc11_compare_op1;
const struct processor_costs *m68hc11_cost;
static const struct processor_costs m6811_cost = {
COSTS_N_INSNS (2),
COSTS_N_INSNS (2),
COSTS_N_INSNS (20),
{ COSTS_N_INSNS (0), COSTS_N_INSNS (1), COSTS_N_INSNS (2),
COSTS_N_INSNS (3), COSTS_N_INSNS (4), COSTS_N_INSNS (3),
COSTS_N_INSNS (2), COSTS_N_INSNS (1) },
{ COSTS_N_INSNS (0), COSTS_N_INSNS (1), COSTS_N_INSNS (4),
COSTS_N_INSNS (6), COSTS_N_INSNS (8), COSTS_N_INSNS (6),
COSTS_N_INSNS (4), COSTS_N_INSNS (2),
COSTS_N_INSNS (2), COSTS_N_INSNS (4),
COSTS_N_INSNS (6), COSTS_N_INSNS (8), COSTS_N_INSNS (10),
COSTS_N_INSNS (8), COSTS_N_INSNS (6), COSTS_N_INSNS (4)
},
COSTS_N_INSNS (20),
COSTS_N_INSNS (20 * 4),
COSTS_N_INSNS (20 * 16),
COSTS_N_INSNS (20),
COSTS_N_INSNS (80),
COSTS_N_INSNS (100)
};
static const struct processor_costs m6812_cost = {
COSTS_N_INSNS (2),
COSTS_N_INSNS (2),
COSTS_N_INSNS (20),
{ COSTS_N_INSNS (0), COSTS_N_INSNS (1), COSTS_N_INSNS (2),
COSTS_N_INSNS (3), COSTS_N_INSNS (4), COSTS_N_INSNS (3),
COSTS_N_INSNS (2), COSTS_N_INSNS (1) },
{ COSTS_N_INSNS (0), COSTS_N_INSNS (1), COSTS_N_INSNS (4),
COSTS_N_INSNS (6), COSTS_N_INSNS (8), COSTS_N_INSNS (6),
COSTS_N_INSNS (4), COSTS_N_INSNS (2),
COSTS_N_INSNS (2), COSTS_N_INSNS (4), COSTS_N_INSNS (6),
COSTS_N_INSNS (8), COSTS_N_INSNS (10), COSTS_N_INSNS (8),
COSTS_N_INSNS (6), COSTS_N_INSNS (4)
},
COSTS_N_INSNS (3),
COSTS_N_INSNS (3),
COSTS_N_INSNS (3 * 4),
COSTS_N_INSNS (12),
COSTS_N_INSNS (12),
COSTS_N_INSNS (100)
};
const char *m68hc11_regparm_string;
const char *m68hc11_reg_alloc_order;
const char *m68hc11_soft_reg_count;
static int nb_soft_regs;
#undef TARGET_ATTRIBUTE_TABLE
#define TARGET_ATTRIBUTE_TABLE m68hc11_attribute_table
#undef TARGET_ASM_ALIGNED_HI_OP
#define TARGET_ASM_ALIGNED_HI_OP "\t.word\t"
#undef TARGET_ASM_FUNCTION_EPILOGUE
#define TARGET_ASM_FUNCTION_EPILOGUE m68hc11_output_function_epilogue
#undef TARGET_ASM_FILE_START
#define TARGET_ASM_FILE_START m68hc11_file_start
#undef TARGET_ASM_FILE_START_FILE_DIRECTIVE
#define TARGET_ASM_FILE_START_FILE_DIRECTIVE true
#undef TARGET_ENCODE_SECTION_INFO
#define TARGET_ENCODE_SECTION_INFO m68hc11_encode_section_info
#undef TARGET_SECTION_TYPE_FLAGS
#define TARGET_SECTION_TYPE_FLAGS m68hc11_section_type_flags
#undef TARGET_RTX_COSTS
#define TARGET_RTX_COSTS m68hc11_rtx_costs
#undef TARGET_ADDRESS_COST
#define TARGET_ADDRESS_COST m68hc11_address_cost
#undef TARGET_MACHINE_DEPENDENT_REORG
#define TARGET_MACHINE_DEPENDENT_REORG m68hc11_reorg
#undef TARGET_INIT_LIBFUNCS
#define TARGET_INIT_LIBFUNCS m68hc11_init_libfuncs
#undef TARGET_STRUCT_VALUE_RTX
#define TARGET_STRUCT_VALUE_RTX m68hc11_struct_value_rtx
#undef TARGET_RETURN_IN_MEMORY
#define TARGET_RETURN_IN_MEMORY m68hc11_return_in_memory
#undef TARGET_CALLEE_COPIES
#define TARGET_CALLEE_COPIES hook_callee_copies_named
#undef TARGET_STRIP_NAME_ENCODING
#define TARGET_STRIP_NAME_ENCODING m68hc11_strip_name_encoding
struct gcc_target targetm = TARGET_INITIALIZER;
int
m68hc11_override_options (void)
{
memset (m68hc11_reg_valid_for_index, 0,
sizeof (m68hc11_reg_valid_for_index));
memset (m68hc11_reg_valid_for_base, 0, sizeof (m68hc11_reg_valid_for_base));
if (flag_pic)
{
warning ("-f%s ignored for 68HC11/68HC12 (not supported)",
(flag_pic > 1) ? "PIC" : "pic");
flag_pic = 0;
}
flag_web = 0;
if (TARGET_M6811)
{
if (TARGET_DEFAULT != MASK_M6811)
target_flags &= ~TARGET_DEFAULT;
if (!TARGET_M6812)
target_flags &= ~(TARGET_AUTO_INC_DEC | TARGET_MIN_MAX);
m68hc11_cost = &m6811_cost;
m68hc11_min_offset = 0;
m68hc11_max_offset = 256;
m68hc11_index_reg_class = NO_REGS;
m68hc11_base_reg_class = A_REGS;
m68hc11_reg_valid_for_base[HARD_X_REGNUM] = 1;
m68hc11_reg_valid_for_base[HARD_Y_REGNUM] = 1;
m68hc11_reg_valid_for_base[HARD_Z_REGNUM] = 1;
m68hc11_sp_correction = 1;
m68hc11_tmp_regs_class = D_REGS;
m68hc11_addr_mode = ADDR_OFFSET;
m68hc11_mov_addr_mode = 0;
if (m68hc11_soft_reg_count == 0 && !TARGET_M6812)
m68hc11_soft_reg_count = "4";
}
if (TARGET_M6812)
{
m68hc11_cost = &m6812_cost;
m68hc11_min_offset = -65536;
m68hc11_max_offset = 65536;
m68hc11_index_reg_class = D_REGS;
m68hc11_base_reg_class = A_OR_SP_REGS;
m68hc11_reg_valid_for_base[HARD_X_REGNUM] = 1;
m68hc11_reg_valid_for_base[HARD_Y_REGNUM] = 1;
m68hc11_reg_valid_for_base[HARD_Z_REGNUM] = 1;
m68hc11_reg_valid_for_base[HARD_SP_REGNUM] = 1;
m68hc11_reg_valid_for_index[HARD_D_REGNUM] = 1;
m68hc11_sp_correction = 0;
m68hc11_tmp_regs_class = TMP_REGS;
m68hc11_addr_mode = ADDR_INDIRECT | ADDR_OFFSET | ADDR_CONST
| (TARGET_AUTO_INC_DEC ? ADDR_INCDEC : 0);
m68hc11_mov_addr_mode = ADDR_OFFSET | ADDR_CONST
| (TARGET_AUTO_INC_DEC ? ADDR_INCDEC : 0);
target_flags &= ~MASK_M6811;
target_flags |= MASK_NO_DIRECT_MODE;
if (m68hc11_soft_reg_count == 0)
m68hc11_soft_reg_count = "0";
if (TARGET_LONG_CALLS)
current_function_far = 1;
}
return 0;
}
void
m68hc11_conditional_register_usage (void)
{
int i;
int cnt = atoi (m68hc11_soft_reg_count);
if (cnt < 0)
cnt = 0;
if (cnt > SOFT_REG_LAST - SOFT_REG_FIRST)
cnt = SOFT_REG_LAST - SOFT_REG_FIRST;
nb_soft_regs = cnt;
for (i = SOFT_REG_FIRST + cnt; i < SOFT_REG_LAST; i++)
{
fixed_regs[i] = 1;
call_used_regs[i] = 1;
}
if (TARGET_M6812 && flag_omit_frame_pointer && optimize)
{
fixed_regs[HARD_Z_REGNUM] = 1;
}
}
void
create_regs_rtx (void)
{
ix_reg = gen_rtx_REG (HImode, HARD_X_REGNUM);
iy_reg = gen_rtx_REG (HImode, HARD_Y_REGNUM);
d_reg = gen_rtx_REG (HImode, HARD_D_REGNUM);
m68hc11_soft_tmp_reg = gen_rtx_REG (HImode, SOFT_TMP_REGNUM);
stack_push_word = gen_rtx_MEM (HImode,
gen_rtx_PRE_DEC (HImode,
gen_rtx_REG (HImode, HARD_SP_REGNUM)));
stack_pop_word = gen_rtx_MEM (HImode,
gen_rtx_POST_INC (HImode,
gen_rtx_REG (HImode, HARD_SP_REGNUM)));
}
int
hard_regno_mode_ok (int regno, enum machine_mode mode)
{
switch (GET_MODE_SIZE (mode))
{
case 8:
return S_REGNO_P (regno) && nb_soft_regs >= 4;
case 4:
return X_REGNO_P (regno) || (S_REGNO_P (regno) && nb_soft_regs >= 2);
case 2:
return G_REGNO_P (regno);
case 1:
return G_REGNO_P (regno) && !SP_REGNO_P (regno);
default:
return 0;
}
}
int
m68hc11_hard_regno_rename_ok (int reg1, int reg2)
{
if (reg2 == HARD_Z_REGNUM)
return 0;
if (TARGET_M6811 && reg2 == HARD_Y_REGNUM
&& (D_REGNO_P (reg1) || X_REGNO_P (reg1)))
return 0;
return 1;
}
enum reg_class
preferred_reload_class (rtx operand, enum reg_class class)
{
enum machine_mode mode;
mode = GET_MODE (operand);
if (debug_m6811)
{
printf ("Preferred reload: (class=%s): ", reg_class_names[class]);
}
if (class == D_OR_A_OR_S_REGS && SP_REG_P (operand))
return m68hc11_base_reg_class;
if (class >= S_REGS && (GET_CODE (operand) == MEM
|| GET_CODE (operand) == CONST_INT))
{
switch (class)
{
default:
case G_REGS:
case D_OR_A_OR_S_REGS:
class = A_OR_D_REGS;
break;
case A_OR_S_REGS:
class = A_REGS;
break;
case D_OR_SP_OR_S_REGS:
class = D_OR_SP_REGS;
break;
case D_OR_Y_OR_S_REGS:
class = D_OR_Y_REGS;
break;
case D_OR_X_OR_S_REGS:
class = D_OR_X_REGS;
break;
case SP_OR_S_REGS:
class = SP_REGS;
break;
case Y_OR_S_REGS:
class = Y_REGS;
break;
case X_OR_S_REGS:
class = X_REGS;
break;
case D_OR_S_REGS:
class = D_REGS;
}
}
else if (class == Y_REGS && GET_CODE (operand) == MEM)
{
class = Y_REGS;
}
else if (class == A_OR_D_REGS && GET_MODE_SIZE (mode) == 4)
{
class = D_OR_X_REGS;
}
else if (class >= S_REGS && S_REG_P (operand))
{
switch (class)
{
default:
case G_REGS:
case D_OR_A_OR_S_REGS:
class = A_OR_D_REGS;
break;
case A_OR_S_REGS:
class = A_REGS;
break;
case D_OR_SP_OR_S_REGS:
class = D_OR_SP_REGS;
break;
case D_OR_Y_OR_S_REGS:
class = D_OR_Y_REGS;
break;
case D_OR_X_OR_S_REGS:
class = D_OR_X_REGS;
break;
case SP_OR_S_REGS:
class = SP_REGS;
break;
case Y_OR_S_REGS:
class = Y_REGS;
break;
case X_OR_S_REGS:
class = X_REGS;
break;
case D_OR_S_REGS:
class = D_REGS;
}
}
else if (class >= S_REGS)
{
if (debug_m6811)
{
printf ("Class = %s for: ", reg_class_names[class]);
fflush (stdout);
debug_rtx (operand);
}
}
if (debug_m6811)
{
printf (" => class=%s\n", reg_class_names[class]);
fflush (stdout);
debug_rtx (operand);
}
return class;
}
static int
register_indirect_p (rtx operand, enum machine_mode mode, int addr_mode)
{
rtx base, offset;
switch (GET_CODE (operand))
{
case MEM:
if ((addr_mode & ADDR_INDIRECT) && GET_MODE_SIZE (mode) <= 2)
return register_indirect_p (XEXP (operand, 0), mode,
addr_mode & (ADDR_STRICT | ADDR_OFFSET));
return 0;
case POST_INC:
case PRE_INC:
case POST_DEC:
case PRE_DEC:
if (addr_mode & ADDR_INCDEC)
return register_indirect_p (XEXP (operand, 0), mode,
addr_mode & ADDR_STRICT);
return 0;
case PLUS:
base = XEXP (operand, 0);
if (GET_CODE (base) == MEM)
return 0;
offset = XEXP (operand, 1);
if (GET_CODE (offset) == MEM)
return 0;
if (GET_CODE (base) == REG && GET_CODE (offset) == REG)
{
if (!(addr_mode & ADDR_INDEXED))
return 0;
addr_mode &= ADDR_STRICT;
if (REGNO_OK_FOR_BASE_P2 (REGNO (base), addr_mode)
&& REGNO_OK_FOR_INDEX_P2 (REGNO (offset), addr_mode))
return 1;
if (REGNO_OK_FOR_BASE_P2 (REGNO (offset), addr_mode)
&& REGNO_OK_FOR_INDEX_P2 (REGNO (base), addr_mode))
return 1;
return 0;
}
if (!(addr_mode & ADDR_OFFSET))
return 0;
if (GET_CODE (base) == REG)
{
if (!VALID_CONSTANT_OFFSET_P (offset, mode))
return 0;
if (!(addr_mode & ADDR_STRICT))
return 1;
return REGNO_OK_FOR_BASE_P2 (REGNO (base), 1);
}
if (GET_CODE (offset) == REG)
{
if (!VALID_CONSTANT_OFFSET_P (base, mode))
return 0;
if (!(addr_mode & ADDR_STRICT))
return 1;
return REGNO_OK_FOR_BASE_P2 (REGNO (offset), 1);
}
return 0;
case REG:
return REGNO_OK_FOR_BASE_P2 (REGNO (operand), addr_mode & ADDR_STRICT);
case CONST_INT:
if (addr_mode & ADDR_CONST)
return VALID_CONSTANT_OFFSET_P (operand, mode);
return 0;
default:
return 0;
}
}
int
m68hc11_small_indexed_indirect_p (rtx operand, enum machine_mode mode)
{
rtx base, offset;
int addr_mode;
if (GET_CODE (operand) == REG && reload_in_progress
&& REGNO (operand) >= FIRST_PSEUDO_REGISTER
&& reg_equiv_memory_loc[REGNO (operand)])
{
operand = reg_equiv_memory_loc[REGNO (operand)];
operand = eliminate_regs (operand, 0, NULL_RTX);
}
if (GET_CODE (operand) != MEM)
return 0;
operand = XEXP (operand, 0);
if (CONSTANT_ADDRESS_P (operand))
return 1;
if (PUSH_POP_ADDRESS_P (operand))
return 1;
addr_mode = m68hc11_mov_addr_mode | (reload_completed ? ADDR_STRICT : 0);
if (!register_indirect_p (operand, mode, addr_mode))
return 0;
if (TARGET_M6812 && GET_CODE (operand) == PLUS
&& (reload_completed | reload_in_progress))
{
base = XEXP (operand, 0);
offset = XEXP (operand, 1);
if (GET_CODE (base) != CONST_INT && GET_CODE (offset) != CONST_INT)
return 0;
if (GET_CODE (base) == CONST_INT)
offset = base;
switch (GET_MODE_SIZE (mode))
{
case 8:
if (INTVAL (offset) < -16 + 6 || INTVAL (offset) > 15 - 6)
return 0;
break;
case 4:
if (INTVAL (offset) < -16 + 2 || INTVAL (offset) > 15 - 2)
return 0;
break;
default:
if (INTVAL (offset) < -16 || INTVAL (offset) > 15)
return 0;
break;
}
}
return 1;
}
int
m68hc11_register_indirect_p (rtx operand, enum machine_mode mode)
{
int addr_mode;
if (GET_CODE (operand) == REG && reload_in_progress
&& REGNO (operand) >= FIRST_PSEUDO_REGISTER
&& reg_equiv_memory_loc[REGNO (operand)])
{
operand = reg_equiv_memory_loc[REGNO (operand)];
operand = eliminate_regs (operand, 0, NULL_RTX);
}
if (GET_CODE (operand) != MEM)
return 0;
operand = XEXP (operand, 0);
addr_mode = m68hc11_addr_mode | (reload_completed ? ADDR_STRICT : 0);
return register_indirect_p (operand, mode, addr_mode);
}
static int
go_if_legitimate_address_internal (rtx operand, enum machine_mode mode,
int strict)
{
int addr_mode;
if (CONSTANT_ADDRESS_P (operand) && TARGET_M6812)
{
if (GET_MODE_SIZE (mode) == 8)
return 0;
return 1;
}
addr_mode = m68hc11_addr_mode | (strict ? ADDR_STRICT : 0);
if (register_indirect_p (operand, mode, addr_mode))
{
return 1;
}
if (PUSH_POP_ADDRESS_P (operand))
{
return 1;
}
if (symbolic_memory_operand (operand, mode))
{
return 1;
}
return 0;
}
int
m68hc11_go_if_legitimate_address (rtx operand, enum machine_mode mode,
int strict)
{
int result;
if (debug_m6811)
{
printf ("Checking: ");
fflush (stdout);
debug_rtx (operand);
}
result = go_if_legitimate_address_internal (operand, mode, strict);
if (debug_m6811)
{
printf (" -> %s\n", result == 0 ? "NO" : "YES");
}
if (result == 0)
{
if (debug_m6811)
{
printf ("go_if_legitimate%s, ret 0: %d:",
(strict ? "_strict" : ""), mode);
fflush (stdout);
debug_rtx (operand);
}
}
return result;
}
int
m68hc11_legitimize_address (rtx *operand ATTRIBUTE_UNUSED,
rtx old_operand ATTRIBUTE_UNUSED,
enum machine_mode mode ATTRIBUTE_UNUSED)
{
return 0;
}
int
m68hc11_reload_operands (rtx operands[])
{
enum machine_mode mode;
if (regs_inited == 0)
create_regs_rtx ();
mode = GET_MODE (operands[1]);
if (A_REG_P (operands[0]) && memory_reload_operand (operands[1], mode))
{
rtx big_offset = XEXP (XEXP (operands[1], 0), 1);
rtx base = XEXP (XEXP (operands[1], 0), 0);
if (GET_CODE (base) != REG)
{
rtx tmp = base;
base = big_offset;
big_offset = tmp;
}
if (!VALID_CONSTANT_OFFSET_P (big_offset, mode))
{
int vh, vl;
rtx reg = operands[0];
rtx offset;
int val = INTVAL (big_offset);
if (!rtx_equal_p (base, operands[0]))
{
emit_move_insn (reg, base);
}
if (val > 0)
{
vh = val >> 8;
vl = val & 0x0FF;
}
else
{
vh = (val >> 8) & 0x0FF;
vl = val & 0x0FF;
}
offset = GEN_INT (vl);
if (!VALID_CONSTANT_OFFSET_P (offset, mode))
{
emit_insn (gen_rtx_SET (VOIDmode, reg,
gen_rtx_PLUS (HImode, reg, big_offset)));
offset = const0_rtx;
}
else
{
emit_insn (gen_rtx_SET (VOIDmode, reg,
gen_rtx_PLUS (HImode, reg,
GEN_INT (vh << 8))));
}
emit_move_insn (operands[0],
gen_rtx_MEM (GET_MODE (operands[1]),
gen_rtx_PLUS (Pmode, reg, offset)));
return 1;
}
}
return 0;
}
void
m68hc11_emit_libcall (const char *name, enum rtx_code code,
enum machine_mode dmode, enum machine_mode smode,
int noperands, rtx *operands)
{
rtx ret;
rtx insns;
rtx libcall;
rtx equiv;
start_sequence ();
libcall = gen_rtx_SYMBOL_REF (Pmode, name);
switch (noperands)
{
case 2:
ret = emit_library_call_value (libcall, NULL_RTX, LCT_CONST,
dmode, 1, operands[1], smode);
equiv = gen_rtx_fmt_e (code, dmode, operands[1]);
break;
case 3:
ret = emit_library_call_value (libcall, NULL_RTX,
LCT_CONST, dmode, 2,
operands[1], smode, operands[2],
smode);
equiv = gen_rtx_fmt_ee (code, dmode, operands[1], operands[2]);
break;
default:
abort ();
}
insns = get_insns ();
end_sequence ();
emit_libcall_block (insns, operands[0], ret, equiv);
}
static int
m68hc11_auto_inc_p (rtx x)
{
return GET_CODE (x) == PRE_DEC
|| GET_CODE (x) == POST_INC
|| GET_CODE (x) == POST_DEC || GET_CODE (x) == PRE_INC;
}
int
memory_reload_operand (rtx operand, enum machine_mode mode ATTRIBUTE_UNUSED)
{
return GET_CODE (operand) == MEM
&& GET_CODE (XEXP (operand, 0)) == PLUS
&& ((GET_CODE (XEXP (XEXP (operand, 0), 0)) == REG
&& GET_CODE (XEXP (XEXP (operand, 0), 1)) == CONST_INT)
|| (GET_CODE (XEXP (XEXP (operand, 0), 1)) == REG
&& GET_CODE (XEXP (XEXP (operand, 0), 0)) == CONST_INT));
}
int
tst_operand (rtx operand, enum machine_mode mode)
{
if (GET_CODE (operand) == MEM && reload_completed == 0)
{
rtx addr = XEXP (operand, 0);
if (m68hc11_auto_inc_p (addr))
return 0;
}
return nonimmediate_operand (operand, mode);
}
int
cmp_operand (rtx operand, enum machine_mode mode)
{
if (GET_CODE (operand) == MEM)
{
rtx addr = XEXP (operand, 0);
if (m68hc11_auto_inc_p (addr))
return 0;
}
return general_operand (operand, mode);
}
int
non_push_operand (rtx operand, enum machine_mode mode)
{
if (general_operand (operand, mode) == 0)
return 0;
if (push_operand (operand, mode) == 1)
return 0;
return 1;
}
int
splitable_operand (rtx operand, enum machine_mode mode)
{
if (general_operand (operand, mode) == 0)
return 0;
if (push_operand (operand, mode) == 1)
return 0;
if (mode != QImode && GET_CODE (operand) == MEM
&& GET_CODE (XEXP (operand, 0)) == MEM)
return 0;
return 1;
}
int
reg_or_some_mem_operand (rtx operand, enum machine_mode mode)
{
if (GET_CODE (operand) == MEM)
{
rtx op = XEXP (operand, 0);
int addr_mode;
if (symbolic_memory_operand (op, mode))
return 1;
if (IS_STACK_PUSH (operand))
return 1;
if (GET_CODE (operand) == REG && reload_in_progress
&& REGNO (operand) >= FIRST_PSEUDO_REGISTER
&& reg_equiv_memory_loc[REGNO (operand)])
{
operand = reg_equiv_memory_loc[REGNO (operand)];
operand = eliminate_regs (operand, 0, NULL_RTX);
}
if (GET_CODE (operand) != MEM)
return 0;
operand = XEXP (operand, 0);
addr_mode = m68hc11_addr_mode | (reload_completed ? ADDR_STRICT : 0);
addr_mode &= ~ADDR_INDIRECT;
return register_indirect_p (operand, mode, addr_mode);
}
return register_operand (operand, mode);
}
int
m68hc11_symbolic_p (rtx operand, enum machine_mode mode)
{
if (GET_CODE (operand) == MEM)
{
rtx op = XEXP (operand, 0);
if (symbolic_memory_operand (op, mode))
return 1;
}
return 0;
}
int
m68hc11_indirect_p (rtx operand, enum machine_mode mode)
{
if (GET_CODE (operand) == MEM && GET_MODE (operand) == mode)
{
rtx op = XEXP (operand, 0);
int addr_mode;
if (m68hc11_page0_symbol_p (op))
return 1;
if (symbolic_memory_operand (op, mode))
return TARGET_M6812;
if (reload_in_progress)
return 1;
operand = XEXP (operand, 0);
addr_mode = m68hc11_addr_mode | (reload_completed ? ADDR_STRICT : 0);
return register_indirect_p (operand, mode, addr_mode);
}
return 0;
}
int
stack_register_operand (rtx operand, enum machine_mode mode ATTRIBUTE_UNUSED)
{
return SP_REG_P (operand);
}
int
d_register_operand (rtx operand, enum machine_mode mode)
{
if (GET_MODE (operand) != mode && mode != VOIDmode)
return 0;
if (GET_CODE (operand) == SUBREG)
operand = XEXP (operand, 0);
return GET_CODE (operand) == REG
&& (REGNO (operand) >= FIRST_PSEUDO_REGISTER
|| REGNO (operand) == HARD_D_REGNUM
|| (mode == QImode && REGNO (operand) == HARD_B_REGNUM));
}
int
hard_addr_reg_operand (rtx operand, enum machine_mode mode)
{
if (GET_MODE (operand) != mode && mode != VOIDmode)
return 0;
if (GET_CODE (operand) == SUBREG)
operand = XEXP (operand, 0);
return GET_CODE (operand) == REG
&& (REGNO (operand) == HARD_X_REGNUM
|| REGNO (operand) == HARD_Y_REGNUM
|| REGNO (operand) == HARD_Z_REGNUM);
}
int
hard_reg_operand (rtx operand, enum machine_mode mode)
{
if (GET_MODE (operand) != mode && mode != VOIDmode)
return 0;
if (GET_CODE (operand) == SUBREG)
operand = XEXP (operand, 0);
return GET_CODE (operand) == REG
&& (REGNO (operand) >= FIRST_PSEUDO_REGISTER
|| H_REGNO_P (REGNO (operand)));
}
int
memory_indexed_operand (rtx operand, enum machine_mode mode ATTRIBUTE_UNUSED)
{
if (GET_CODE (operand) != MEM)
return 0;
operand = XEXP (operand, 0);
if (GET_CODE (operand) == PLUS)
{
if (GET_CODE (XEXP (operand, 0)) == REG)
operand = XEXP (operand, 0);
else if (GET_CODE (XEXP (operand, 1)) == REG)
operand = XEXP (operand, 1);
}
return GET_CODE (operand) == REG
&& (REGNO (operand) >= FIRST_PSEUDO_REGISTER
|| A_REGNO_P (REGNO (operand)));
}
int
push_pop_operand_p (rtx operand)
{
if (GET_CODE (operand) != MEM)
{
return 0;
}
operand = XEXP (operand, 0);
return PUSH_POP_ADDRESS_P (operand);
}
int
symbolic_memory_operand (rtx op, enum machine_mode mode)
{
switch (GET_CODE (op))
{
case SYMBOL_REF:
case LABEL_REF:
return 1;
case CONST:
op = XEXP (op, 0);
return ((GET_CODE (XEXP (op, 0)) == SYMBOL_REF
|| GET_CODE (XEXP (op, 0)) == LABEL_REF)
&& GET_CODE (XEXP (op, 1)) == CONST_INT);
case CONST_DOUBLE:
return GET_MODE (op) == mode;
case PLUS:
return symbolic_memory_operand (XEXP (op, 0), mode)
&& symbolic_memory_operand (XEXP (op, 1), mode);
default:
return 0;
}
}
int
m68hc11_eq_compare_operator (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
{
return GET_CODE (op) == EQ || GET_CODE (op) == NE;
}
int
m68hc11_logical_operator (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
{
return GET_CODE (op) == AND || GET_CODE (op) == IOR || GET_CODE (op) == XOR;
}
int
m68hc11_arith_operator (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
{
return GET_CODE (op) == AND || GET_CODE (op) == IOR || GET_CODE (op) == XOR
|| GET_CODE (op) == PLUS || GET_CODE (op) == MINUS
|| GET_CODE (op) == ASHIFT || GET_CODE (op) == ASHIFTRT
|| GET_CODE (op) == LSHIFTRT || GET_CODE (op) == ROTATE
|| GET_CODE (op) == ROTATERT;
}
int
m68hc11_non_shift_operator (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
{
return GET_CODE (op) == AND || GET_CODE (op) == IOR || GET_CODE (op) == XOR
|| GET_CODE (op) == PLUS || GET_CODE (op) == MINUS;
}
int
m68hc11_shift_operator (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
{
return GET_CODE (op) == ROTATE || GET_CODE (op) == ROTATERT
|| GET_CODE (op) == LSHIFTRT || GET_CODE (op) == ASHIFT
|| GET_CODE (op) == ASHIFTRT;
}
int
m68hc11_unary_operator (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
{
return GET_CODE (op) == NEG || GET_CODE (op) == NOT
|| GET_CODE (op) == SIGN_EXTEND || GET_CODE (op) == ZERO_EXTEND;
}
void
m68hc11_initialize_trampoline (rtx tramp, rtx fnaddr, rtx cxt)
{
const char *static_chain_reg = reg_names[STATIC_CHAIN_REGNUM];
if (*static_chain_reg == '*')
static_chain_reg++;
if (TARGET_M6811)
{
emit_move_insn (gen_rtx_MEM (HImode, tramp), GEN_INT (0x18ce));
emit_move_insn (gen_rtx_MEM (HImode, plus_constant (tramp, 2)), cxt);
emit_move_insn (gen_rtx_MEM (HImode, plus_constant (tramp, 4)),
GEN_INT (0x18df));
emit_move_insn (gen_rtx_MEM (QImode, plus_constant (tramp, 6)),
gen_rtx_CONST (QImode,
gen_rtx_SYMBOL_REF (Pmode,
static_chain_reg)));
emit_move_insn (gen_rtx_MEM (QImode, plus_constant (tramp, 7)),
GEN_INT (0x7e));
emit_move_insn (gen_rtx_MEM (HImode, plus_constant (tramp, 8)), fnaddr);
}
else
{
emit_move_insn (gen_rtx_MEM (HImode, tramp), GEN_INT (0x1803));
emit_move_insn (gen_rtx_MEM (HImode, plus_constant (tramp, 2)), cxt);
emit_move_insn (gen_rtx_MEM (HImode, plus_constant (tramp, 4)),
gen_rtx_CONST (HImode,
gen_rtx_SYMBOL_REF (Pmode,
static_chain_reg)));
emit_move_insn (gen_rtx_MEM (QImode, plus_constant (tramp, 6)),
GEN_INT (0x06));
emit_move_insn (gen_rtx_MEM (HImode, plus_constant (tramp, 7)), fnaddr);
}
}
static tree
m68hc11_handle_page0_attribute (tree *node, tree name,
tree args ATTRIBUTE_UNUSED,
int flags ATTRIBUTE_UNUSED, bool *no_add_attrs)
{
tree decl = *node;
if (TREE_STATIC (decl) || DECL_EXTERNAL (decl))
{
DECL_SECTION_NAME (decl) = build_string (6, ".page0");
}
else
{
warning ("%qs attribute ignored", IDENTIFIER_POINTER (name));
*no_add_attrs = true;
}
return NULL_TREE;
}
const struct attribute_spec m68hc11_attribute_table[] =
{
{ "interrupt", 0, 0, false, true, true, m68hc11_handle_fntype_attribute },
{ "trap", 0, 0, false, true, true, m68hc11_handle_fntype_attribute },
{ "far", 0, 0, false, true, true, m68hc11_handle_fntype_attribute },
{ "near", 0, 0, false, true, true, m68hc11_handle_fntype_attribute },
{ "page0", 0, 0, false, false, false, m68hc11_handle_page0_attribute },
{ NULL, 0, 0, false, false, false, NULL }
};
static rtx trap_handler_symbol = 0;
static tree
m68hc11_handle_fntype_attribute (tree *node, tree name,
tree args ATTRIBUTE_UNUSED,
int flags ATTRIBUTE_UNUSED,
bool *no_add_attrs)
{
if (TREE_CODE (*node) != FUNCTION_TYPE
&& TREE_CODE (*node) != METHOD_TYPE
&& TREE_CODE (*node) != FIELD_DECL
&& TREE_CODE (*node) != TYPE_DECL)
{
warning ("%qs attribute only applies to functions",
IDENTIFIER_POINTER (name));
*no_add_attrs = true;
}
return NULL_TREE;
}
static const char *
m68hc11_strip_name_encoding (const char *str)
{
return str + (*str == '*' || *str == '@' || *str == '&');
}
static void
m68hc11_encode_label (tree decl)
{
const char *str = XSTR (XEXP (DECL_RTL (decl), 0), 0);
int len = strlen (str);
char *newstr = alloca (len + 2);
newstr[0] = '@';
strcpy (&newstr[1], str);
XSTR (XEXP (DECL_RTL (decl), 0), 0) = ggc_alloc_string (newstr, len + 1);
}
int
m68hc11_page0_symbol_p (rtx x)
{
switch (GET_CODE (x))
{
case SYMBOL_REF:
return XSTR (x, 0) != 0 && XSTR (x, 0)[0] == '@';
case CONST:
return m68hc11_page0_symbol_p (XEXP (x, 0));
case PLUS:
if (!m68hc11_page0_symbol_p (XEXP (x, 0)))
return 0;
return GET_CODE (XEXP (x, 1)) == CONST_INT
&& INTVAL (XEXP (x, 1)) < 256
&& INTVAL (XEXP (x, 1)) >= 0;
default:
return 0;
}
}
static void
m68hc11_encode_section_info (tree decl, rtx rtl, int first ATTRIBUTE_UNUSED)
{
tree func_attr;
int trap_handler;
int is_far = 0;
if (TREE_CODE (decl) == VAR_DECL)
{
if (lookup_attribute ("page0", DECL_ATTRIBUTES (decl)) != 0)
m68hc11_encode_label (decl);
return;
}
if (TREE_CODE (decl) != FUNCTION_DECL)
return;
func_attr = TYPE_ATTRIBUTES (TREE_TYPE (decl));
if (lookup_attribute ("far", func_attr) != NULL_TREE)
is_far = 1;
else if (lookup_attribute ("near", func_attr) == NULL_TREE)
is_far = TARGET_LONG_CALLS != 0;
trap_handler = lookup_attribute ("trap", func_attr) != NULL_TREE;
if (trap_handler && is_far)
{
warning ("%<trap%> and %<far%> attributes are not compatible, ignoring %<far%>");
trap_handler = 0;
}
if (trap_handler)
{
if (trap_handler_symbol != 0)
warning ("%<trap%> attribute is already used");
else
trap_handler_symbol = XEXP (rtl, 0);
}
SYMBOL_REF_FLAG (XEXP (rtl, 0)) = is_far;
}
static unsigned int
m68hc11_section_type_flags (tree decl, const char *name, int reloc)
{
unsigned int flags = default_section_type_flags (decl, name, reloc);
if (strncmp (name, ".eeprom", 7) == 0)
{
flags |= SECTION_WRITE | SECTION_CODE | SECTION_OVERRIDE;
}
return flags;
}
int
m68hc11_is_far_symbol (rtx sym)
{
if (GET_CODE (sym) == MEM)
sym = XEXP (sym, 0);
return SYMBOL_REF_FLAG (sym);
}
int
m68hc11_is_trap_symbol (rtx sym)
{
if (GET_CODE (sym) == MEM)
sym = XEXP (sym, 0);
return trap_handler_symbol != 0 && rtx_equal_p (trap_handler_symbol, sym);
}
int
m68hc11_initial_elimination_offset (int from, int to)
{
int trap_handler;
tree func_attr;
int size;
int regno;
func_attr = TYPE_ATTRIBUTES (TREE_TYPE (current_function_decl));
current_function_interrupt = lookup_attribute ("interrupt",
func_attr) != NULL_TREE;
trap_handler = lookup_attribute ("trap", func_attr) != NULL_TREE;
if (lookup_attribute ("far", func_attr) != 0)
current_function_far = 1;
else if (lookup_attribute ("near", func_attr) != 0)
current_function_far = 0;
else
current_function_far = (TARGET_LONG_CALLS != 0
&& !current_function_interrupt
&& !trap_handler);
if (trap_handler && from == ARG_POINTER_REGNUM)
size = 7;
else if (current_function_far && from == ARG_POINTER_REGNUM)
size = 1;
else
size = 0;
if (from == ARG_POINTER_REGNUM && to == HARD_FRAME_POINTER_REGNUM)
{
return get_frame_size () + 2 + m68hc11_sp_correction + size;
}
if (from == FRAME_POINTER_REGNUM && to == HARD_FRAME_POINTER_REGNUM)
{
return m68hc11_sp_correction;
}
for (regno = SOFT_REG_FIRST; regno < SOFT_REG_LAST; regno++)
{
if (regs_ever_live[regno] && !call_used_regs[regno])
{
size += 2;
}
}
if (from == ARG_POINTER_REGNUM && to == HARD_SP_REGNUM)
{
return get_frame_size () + size;
}
if (from == FRAME_POINTER_REGNUM && to == HARD_SP_REGNUM)
{
return size;
}
return 0;
}
void
m68hc11_init_cumulative_args (CUMULATIVE_ARGS *cum, tree fntype, rtx libname)
{
tree ret_type;
z_replacement_completed = 0;
cum->words = 0;
cum->nregs = 0;
if (fntype == 0)
{
const char *name;
size_t len;
if (libname == 0 || GET_CODE (libname) != SYMBOL_REF)
return;
name = XSTR (libname, 0);
len = strlen (name);
if (len > 3
&& ((name[len - 2] == 'd'
&& (name[len - 1] == 'f' || name[len - 1] == 'i'))
|| (name[len - 3] == 'd'
&& (name[len - 2] == 'i' || name[len - 2] == 'f'))))
{
cum->words = 1;
cum->nregs = 1;
}
return;
}
ret_type = TREE_TYPE (fntype);
if (ret_type && aggregate_value_p (ret_type, fntype))
{
cum->words = 1;
cum->nregs = 1;
}
}
void
m68hc11_function_arg_advance (CUMULATIVE_ARGS *cum, enum machine_mode mode,
tree type, int named ATTRIBUTE_UNUSED)
{
if (mode != BLKmode)
{
if (cum->words == 0 && GET_MODE_SIZE (mode) == 4)
{
cum->nregs = 2;
cum->words = GET_MODE_SIZE (mode);
}
else
{
cum->words += GET_MODE_SIZE (mode);
if (cum->words <= HARD_REG_SIZE)
cum->nregs = 1;
}
}
else
{
cum->words += int_size_in_bytes (type);
}
return;
}
struct rtx_def *
m68hc11_function_arg (const CUMULATIVE_ARGS *cum, enum machine_mode mode,
tree type ATTRIBUTE_UNUSED, int named ATTRIBUTE_UNUSED)
{
if (cum->words != 0)
{
return NULL_RTX;
}
if (mode != BLKmode)
{
if (GET_MODE_SIZE (mode) == 2 * HARD_REG_SIZE)
return gen_rtx_REG (mode, HARD_X_REGNUM);
if (GET_MODE_SIZE (mode) > HARD_REG_SIZE)
{
return NULL_RTX;
}
return gen_rtx_REG (mode, HARD_D_REGNUM);
}
return NULL_RTX;
}
int
m68hc11_function_arg_padding (enum machine_mode mode, tree type)
{
if (type != 0 && AGGREGATE_TYPE_P (type))
return upward;
return DEFAULT_FUNCTION_ARG_PADDING (mode, type);
}
static void
emit_move_after_reload (rtx to, rtx from, rtx scratch)
{
rtx insn;
if (TARGET_M6812 || H_REG_P (to) || H_REG_P (from))
{
insn = emit_move_insn (to, from);
}
else
{
emit_move_insn (scratch, from);
insn = emit_move_insn (to, scratch);
}
if (IS_STACK_PUSH (to))
{
REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_INC,
XEXP (XEXP (to, 0), 0),
REG_NOTES (insn));
}
else if (IS_STACK_POP (from))
{
REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_INC,
XEXP (XEXP (from, 0), 0),
REG_NOTES (insn));
}
else if (TARGET_M6811 && SP_REG_P (from))
{
REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_INC,
from,
REG_NOTES (insn));
}
}
int
m68hc11_total_frame_size (void)
{
int size;
int regno;
size = get_frame_size ();
if (current_function_interrupt)
{
size += 3 * HARD_REG_SIZE;
}
if (frame_pointer_needed)
size += HARD_REG_SIZE;
for (regno = SOFT_REG_FIRST; regno <= SOFT_REG_LAST; regno++)
if (regs_ever_live[regno] && !call_used_regs[regno])
size += HARD_REG_SIZE;
return size;
}
static void
m68hc11_output_function_epilogue (FILE *out ATTRIBUTE_UNUSED,
HOST_WIDE_INT size ATTRIBUTE_UNUSED)
{
z_replacement_completed = 0;
}
void
expand_prologue (void)
{
tree func_attr;
int size;
int regno;
rtx scratch;
if (reload_completed != 1)
abort ();
size = get_frame_size ();
create_regs_rtx ();
func_attr = TYPE_ATTRIBUTES (TREE_TYPE (current_function_decl));
current_function_interrupt = lookup_attribute ("interrupt",
func_attr) != NULL_TREE;
current_function_trap = lookup_attribute ("trap", func_attr) != NULL_TREE;
if (lookup_attribute ("far", func_attr) != NULL_TREE)
current_function_far = 1;
else if (lookup_attribute ("near", func_attr) != NULL_TREE)
current_function_far = 0;
else
current_function_far = (TARGET_LONG_CALLS != 0
&& !current_function_interrupt
&& !current_function_trap);
if (current_function_args_info.nregs == 2)
scratch = iy_reg;
else
scratch = ix_reg;
if (frame_pointer_needed)
emit_move_after_reload (stack_push_word, hard_frame_pointer_rtx, scratch);
if (current_function_interrupt)
{
emit_move_after_reload (stack_push_word, m68hc11_soft_tmp_reg, scratch);
emit_move_after_reload (stack_push_word,
gen_rtx_REG (HImode, SOFT_Z_REGNUM), scratch);
emit_move_after_reload (stack_push_word,
gen_rtx_REG (HImode, SOFT_SAVED_XY_REGNUM),
scratch);
}
if (TARGET_M6812 && (size > 4 || size == 3))
{
emit_insn (gen_addhi3 (stack_pointer_rtx,
stack_pointer_rtx, GEN_INT (-size)));
}
else if ((!optimize_size && size > 8) || (optimize_size && size > 10))
{
rtx insn;
insn = gen_rtx_PARALLEL
(VOIDmode,
gen_rtvec (2,
gen_rtx_SET (VOIDmode,
stack_pointer_rtx,
gen_rtx_PLUS (HImode,
stack_pointer_rtx,
GEN_INT (-size))),
gen_rtx_CLOBBER (VOIDmode, scratch)));
emit_insn (insn);
}
else
{
int i;
for (i = 2; i <= size; i += 2)
emit_move_after_reload (stack_push_word, ix_reg, 0);
if (size & 1)
emit_insn (gen_addhi3 (stack_pointer_rtx,
stack_pointer_rtx, constm1_rtx));
}
if (frame_pointer_needed)
emit_move_after_reload (hard_frame_pointer_rtx,
stack_pointer_rtx, scratch);
for (regno = SOFT_REG_FIRST; regno <= SOFT_REG_LAST; regno++)
{
if (regs_ever_live[regno] && !call_used_regs[regno])
{
emit_move_after_reload (stack_push_word,
gen_rtx_REG (HImode, regno), scratch);
}
}
}
void
expand_epilogue (void)
{
int size;
register int regno;
int return_size;
rtx scratch;
if (reload_completed != 1)
abort ();
size = get_frame_size ();
if (current_function_return_rtx == 0)
return_size = 0;
else if (GET_CODE (current_function_return_rtx) == MEM)
return_size = HARD_REG_SIZE;
else
return_size = GET_MODE_SIZE (GET_MODE (current_function_return_rtx));
if (return_size > HARD_REG_SIZE && return_size <= 2 * HARD_REG_SIZE)
scratch = iy_reg;
else
scratch = ix_reg;
for (regno = SOFT_REG_LAST; regno >= SOFT_REG_FIRST; regno--)
{
if (regs_ever_live[regno] && !call_used_regs[regno])
{
emit_move_after_reload (gen_rtx_REG (HImode, regno),
stack_pop_word, scratch);
}
}
if (TARGET_M6812 && (size > 4 || size == 3))
{
emit_insn (gen_addhi3 (stack_pointer_rtx,
stack_pointer_rtx, GEN_INT (size)));
}
else if ((!optimize_size && size > 8) || (optimize_size && size > 10))
{
rtx insn;
insn = gen_rtx_PARALLEL
(VOIDmode,
gen_rtvec (2,
gen_rtx_SET (VOIDmode,
stack_pointer_rtx,
gen_rtx_PLUS (HImode,
stack_pointer_rtx,
GEN_INT (size))),
gen_rtx_CLOBBER (VOIDmode, scratch)));
emit_insn (insn);
}
else
{
int i;
for (i = 2; i <= size; i += 2)
emit_move_after_reload (scratch, stack_pop_word, scratch);
if (size & 1)
emit_insn (gen_addhi3 (stack_pointer_rtx,
stack_pointer_rtx, const1_rtx));
}
if (current_function_interrupt)
{
emit_move_after_reload (gen_rtx_REG (HImode, SOFT_SAVED_XY_REGNUM),
stack_pop_word, scratch);
emit_move_after_reload (gen_rtx_REG (HImode, SOFT_Z_REGNUM),
stack_pop_word, scratch);
emit_move_after_reload (m68hc11_soft_tmp_reg, stack_pop_word, scratch);
}
if (frame_pointer_needed)
emit_move_after_reload (hard_frame_pointer_rtx, stack_pop_word, scratch);
else if (current_function_trap && return_size != 0)
{
rtx addr_reg = stack_pointer_rtx;
if (!TARGET_M6812)
{
emit_move_after_reload (scratch, stack_pointer_rtx, 0);
addr_reg = scratch;
}
emit_move_after_reload (gen_rtx_MEM (HImode,
gen_rtx_PLUS (HImode, addr_reg,
const1_rtx)), d_reg, 0);
if (return_size > HARD_REG_SIZE)
emit_move_after_reload (gen_rtx_MEM (HImode,
gen_rtx_PLUS (HImode, addr_reg,
GEN_INT (3))), ix_reg, 0);
}
emit_jump_insn (gen_return ());
}
rtx
m68hc11_gen_lowpart (enum machine_mode mode, rtx x)
{
if (GET_CODE (x) == MEM && m68hc11_auto_inc_p (XEXP (x, 0)))
{
return gen_rtx_MEM (mode, XEXP (x, 0));
}
if (GET_CODE (x) == CONST_DOUBLE)
{
long l[2];
if (GET_MODE_CLASS (GET_MODE (x)) == MODE_FLOAT)
{
REAL_VALUE_TYPE r;
if (GET_MODE (x) == SFmode)
{
REAL_VALUE_FROM_CONST_DOUBLE (r, x);
REAL_VALUE_TO_TARGET_SINGLE (r, l[0]);
}
else
{
rtx first, second;
split_double (x, &first, &second);
return second;
}
if (mode == SImode)
return GEN_INT (l[0]);
return gen_int_mode (l[0], HImode);
}
else
{
l[0] = CONST_DOUBLE_LOW (x);
}
if (mode == SImode)
return GEN_INT (l[0]);
else if (mode == HImode && GET_MODE (x) == SFmode)
return gen_int_mode (l[0], HImode);
else
abort ();
}
if (mode == QImode && D_REG_P (x))
return gen_rtx_REG (mode, HARD_B_REGNUM);
if (GET_CODE (x) == SUBREG && SUBREG_BYTE (x) != 0)
{
if (mode == SImode)
return gen_rtx_SUBREG (mode, SUBREG_REG (x), SUBREG_BYTE (x) + 4);
else if (mode == HImode)
return gen_rtx_SUBREG (mode, SUBREG_REG (x), SUBREG_BYTE (x) + 2);
else
abort ();
}
x = gen_lowpart (mode, x);
if (GET_CODE (x) == MEM)
x = copy_rtx (x);
return x;
}
rtx
m68hc11_gen_highpart (enum machine_mode mode, rtx x)
{
if (GET_CODE (x) == MEM && m68hc11_auto_inc_p (XEXP (x, 0)))
{
return gen_rtx_MEM (mode, XEXP (x, 0));
}
if (GET_CODE (x) == CONST_DOUBLE)
{
long l[2];
if (GET_MODE_CLASS (GET_MODE (x)) == MODE_FLOAT)
{
REAL_VALUE_TYPE r;
if (GET_MODE (x) == SFmode)
{
REAL_VALUE_FROM_CONST_DOUBLE (r, x);
REAL_VALUE_TO_TARGET_SINGLE (r, l[1]);
}
else
{
rtx first, second;
split_double (x, &first, &second);
return first;
}
if (mode == SImode)
return GEN_INT (l[1]);
return gen_int_mode ((l[1] >> 16), HImode);
}
else
{
l[1] = CONST_DOUBLE_HIGH (x);
}
if (mode == SImode)
return GEN_INT (l[1]);
else if (mode == HImode && GET_MODE_CLASS (GET_MODE (x)) == MODE_FLOAT)
return gen_int_mode ((l[0] >> 16), HImode);
else
abort ();
}
if (GET_CODE (x) == CONST_INT)
{
HOST_WIDE_INT val = INTVAL (x);
if (mode == QImode)
{
return gen_int_mode (val >> 8, QImode);
}
else if (mode == HImode)
{
return gen_int_mode (val >> 16, HImode);
}
else if (mode == SImode)
{
return gen_int_mode (val >> 32, SImode);
}
}
if (mode == QImode && D_REG_P (x))
return gen_rtx_REG (mode, HARD_A_REGNUM);
if (mode == QImode && S_REG_P (x))
{
int pos;
pos = TARGET_NO_DIRECT_MODE ? 1 : 0;
return gen_rtx_MEM (QImode,
gen_rtx_SYMBOL_REF (Pmode,
®_names[REGNO (x)][pos]));
}
if (GET_CODE (x) == SUBREG)
{
return gen_rtx_SUBREG (mode, XEXP (x, 0), XEXP (x, 1));
}
if (GET_CODE (x) == REG)
{
if (REGNO (x) < FIRST_PSEUDO_REGISTER)
return gen_rtx_REG (mode, REGNO (x));
else
return gen_rtx_SUBREG (mode, x, 0);
}
if (GET_CODE (x) == MEM)
{
x = change_address (x, mode, 0);
if (GET_CODE (x) == MEM)
x = copy_rtx (x);
return x;
}
abort ();
}
int
dead_register_here (rtx x, rtx reg)
{
rtx x_reg;
rtx p;
if (D_REG_P (reg))
x_reg = gen_rtx_REG (SImode, HARD_X_REGNUM);
else
x_reg = 0;
for (p = PREV_INSN (x); p && GET_CODE (p) != CODE_LABEL; p = PREV_INSN (p))
if (INSN_P (p))
{
rtx body;
body = PATTERN (p);
if (GET_CODE (body) == CALL_INSN)
break;
if (GET_CODE (body) == JUMP_INSN)
break;
if (GET_CODE (body) == SET)
{
rtx dst = XEXP (body, 0);
if (GET_CODE (dst) == REG && REGNO (dst) == REGNO (reg))
break;
if (x_reg && rtx_equal_p (dst, x_reg))
break;
if (find_regno_note (p, REG_DEAD, REGNO (reg)))
return 1;
}
else if (reg_mentioned_p (reg, p)
|| (x_reg && reg_mentioned_p (x_reg, p)))
break;
}
for (p = x ; p; p = NEXT_INSN (p))
{
rtx body;
if (GET_CODE (p) == CODE_LABEL
|| GET_CODE (p) == JUMP_INSN
|| GET_CODE (p) == CALL_INSN || GET_CODE (p) == BARRIER)
break;
if (GET_CODE (p) != INSN)
continue;
body = PATTERN (p);
if (GET_CODE (body) == SET)
{
rtx src = XEXP (body, 1);
rtx dst = XEXP (body, 0);
if (GET_CODE (dst) == REG
&& REGNO (dst) == REGNO (reg) && !reg_mentioned_p (reg, src))
return 1;
}
if (reg_mentioned_p (reg, p)
|| (x_reg != 0 && GET_MODE (p) == SImode
&& reg_mentioned_p (x_reg, p)))
break;
}
return p == 0 ? 1 : 0;
}
static void
asm_print_register (FILE *file, int regno)
{
const char *name = reg_names[regno];
if (TARGET_NO_DIRECT_MODE && name[0] == '*')
name++;
fprintf (file, "%s", name);
}
void
print_operand (FILE *file, rtx op, int letter)
{
if (letter == 't')
{
asm_print_register (file, SOFT_TMP_REGNUM);
return;
}
else if (letter == 'T')
{
asm_print_register (file, SOFT_TMP_REGNUM);
fprintf (file, "+1");
return;
}
else if (letter == '#')
{
asm_fprintf (file, "%I");
}
if (GET_CODE (op) == REG)
{
if (letter == 'b' && S_REG_P (op))
{
asm_print_register (file, REGNO (op));
fprintf (file, "+1");
}
else if (letter == 'b' && D_REG_P (op))
{
asm_print_register (file, HARD_B_REGNUM);
}
else
{
asm_print_register (file, REGNO (op));
}
return;
}
if (GET_CODE (op) == SYMBOL_REF && (letter == 'b' || letter == 'h'))
{
if (letter == 'b')
asm_fprintf (file, "%I%%lo(");
else
asm_fprintf (file, "%I%%hi(");
output_addr_const (file, op);
fprintf (file, ")");
return;
}
if (GET_MODE (op) == HImode || GET_MODE (op) == VOIDmode)
{
if (letter == 'b')
{
op = m68hc11_gen_lowpart (QImode, op);
}
else if (letter == 'h')
{
op = m68hc11_gen_highpart (QImode, op);
}
}
if (GET_CODE (op) == MEM)
{
rtx base = XEXP (op, 0);
switch (GET_CODE (base))
{
case PRE_DEC:
if (TARGET_M6812)
{
fprintf (file, "%u,-", GET_MODE_SIZE (GET_MODE (op)));
asm_print_register (file, REGNO (XEXP (base, 0)));
}
else
abort ();
break;
case POST_DEC:
if (TARGET_M6812)
{
fprintf (file, "%u,", GET_MODE_SIZE (GET_MODE (op)));
asm_print_register (file, REGNO (XEXP (base, 0)));
fprintf (file, "-");
}
else
abort ();
break;
case POST_INC:
if (TARGET_M6812)
{
fprintf (file, "%u,", GET_MODE_SIZE (GET_MODE (op)));
asm_print_register (file, REGNO (XEXP (base, 0)));
fprintf (file, "+");
}
else
abort ();
break;
case PRE_INC:
if (TARGET_M6812)
{
fprintf (file, "%u,+", GET_MODE_SIZE (GET_MODE (op)));
asm_print_register (file, REGNO (XEXP (base, 0)));
}
else
abort ();
break;
case MEM:
if (TARGET_M6812)
{
fprintf (file, "[");
print_operand_address (file, XEXP (base, 0));
fprintf (file, "]");
}
else
abort ();
break;
default:
if (m68hc11_page0_symbol_p (base))
fprintf (file, "*");
output_address (base);
break;
}
}
else if (GET_CODE (op) == CONST_DOUBLE && GET_MODE (op) == SFmode)
{
REAL_VALUE_TYPE r;
long l;
REAL_VALUE_FROM_CONST_DOUBLE (r, op);
REAL_VALUE_TO_TARGET_SINGLE (r, l);
asm_fprintf (file, "%I0x%lx", l);
}
else if (GET_CODE (op) == CONST_DOUBLE && GET_MODE (op) == DFmode)
{
char dstr[30];
real_to_decimal (dstr, CONST_DOUBLE_REAL_VALUE (op),
sizeof (dstr), 0, 1);
asm_fprintf (file, "%I0r%s", dstr);
}
else
{
int need_parenthesize = 0;
if (letter != 'i')
asm_fprintf (file, "%I");
else
need_parenthesize = must_parenthesize (op);
if (need_parenthesize)
fprintf (file, "(");
output_addr_const (file, op);
if (need_parenthesize)
fprintf (file, ")");
}
}
static int
must_parenthesize (rtx op)
{
const char *name;
switch (GET_CODE (op))
{
case SYMBOL_REF:
name = XSTR (op, 0);
return (strcasecmp (name, "a") == 0
|| strcasecmp (name, "b") == 0
|| strcasecmp (name, "d") == 0
|| strcasecmp (name, "x") == 0
|| strcasecmp (name, "y") == 0
|| strcasecmp (name, "ix") == 0
|| strcasecmp (name, "iy") == 0
|| strcasecmp (name, "pc") == 0
|| strcasecmp (name, "sp") == 0
|| strcasecmp (name, "ccr") == 0) ? 1 : 0;
case PLUS:
case MINUS:
return must_parenthesize (XEXP (op, 0))
|| must_parenthesize (XEXP (op, 1));
case MEM:
case CONST:
case ZERO_EXTEND:
case SIGN_EXTEND:
return must_parenthesize (XEXP (op, 0));
case CONST_DOUBLE:
case CONST_INT:
case LABEL_REF:
case CODE_LABEL:
default:
return 0;
}
}
void
print_operand_address (FILE *file, rtx addr)
{
rtx base;
rtx offset;
int need_parenthesis = 0;
switch (GET_CODE (addr))
{
case REG:
if (!REG_P (addr) || !REG_OK_FOR_BASE_STRICT_P (addr))
abort ();
fprintf (file, "0,");
asm_print_register (file, REGNO (addr));
break;
case MEM:
base = XEXP (addr, 0);
switch (GET_CODE (base))
{
case PRE_DEC:
if (TARGET_M6812)
{
fprintf (file, "%u,-", GET_MODE_SIZE (GET_MODE (addr)));
asm_print_register (file, REGNO (XEXP (base, 0)));
}
else
abort ();
break;
case POST_DEC:
if (TARGET_M6812)
{
fprintf (file, "%u,", GET_MODE_SIZE (GET_MODE (addr)));
asm_print_register (file, REGNO (XEXP (base, 0)));
fprintf (file, "-");
}
else
abort ();
break;
case POST_INC:
if (TARGET_M6812)
{
fprintf (file, "%u,", GET_MODE_SIZE (GET_MODE (addr)));
asm_print_register (file, REGNO (XEXP (base, 0)));
fprintf (file, "+");
}
else
abort ();
break;
case PRE_INC:
if (TARGET_M6812)
{
fprintf (file, "%u,+", GET_MODE_SIZE (GET_MODE (addr)));
asm_print_register (file, REGNO (XEXP (base, 0)));
}
else
abort ();
break;
default:
need_parenthesis = must_parenthesize (base);
if (need_parenthesis)
fprintf (file, "(");
output_addr_const (file, base);
if (need_parenthesis)
fprintf (file, ")");
break;
}
break;
case PLUS:
base = XEXP (addr, 0);
offset = XEXP (addr, 1);
if (!G_REG_P (base) && G_REG_P (offset))
{
base = XEXP (addr, 1);
offset = XEXP (addr, 0);
}
if ((CONSTANT_ADDRESS_P (base)) && (CONSTANT_ADDRESS_P (offset)))
{
need_parenthesis = must_parenthesize (addr);
if (need_parenthesis)
fprintf (file, "(");
output_addr_const (file, base);
fprintf (file, "+");
output_addr_const (file, offset);
if (need_parenthesis)
fprintf (file, ")");
}
else if (REG_P (base) && REG_OK_FOR_BASE_STRICT_P (base))
{
if (REG_P (offset))
{
if (TARGET_M6812)
{
asm_print_register (file, REGNO (offset));
fprintf (file, ",");
asm_print_register (file, REGNO (base));
}
else
abort ();
}
else
{
need_parenthesis = must_parenthesize (offset);
if (need_parenthesis)
fprintf (file, "(");
output_addr_const (file, offset);
if (need_parenthesis)
fprintf (file, ")");
fprintf (file, ",");
asm_print_register (file, REGNO (base));
}
}
else
{
abort ();
}
break;
default:
if (GET_CODE (addr) == CONST_INT
&& INTVAL (addr) < 0x8000 && INTVAL (addr) >= -0x8000)
{
fprintf (file, HOST_WIDE_INT_PRINT_DEC, INTVAL (addr));
}
else
{
need_parenthesis = must_parenthesize (addr);
if (need_parenthesis)
fprintf (file, "(");
output_addr_const (file, addr);
if (need_parenthesis)
fprintf (file, ")");
}
break;
}
}
static rtx
m68hc11_expand_compare (enum rtx_code code, rtx op0, rtx op1)
{
rtx ret = 0;
if (GET_MODE_CLASS (GET_MODE (op0)) == MODE_FLOAT)
abort ();
else
{
emit_insn (gen_rtx_SET (VOIDmode, cc0_rtx,
gen_rtx_COMPARE (VOIDmode, op0, op1)));
ret = gen_rtx_fmt_ee (code, VOIDmode, cc0_rtx, const0_rtx);
}
return ret;
}
rtx
m68hc11_expand_compare_and_branch (enum rtx_code code, rtx op0, rtx op1,
rtx label)
{
rtx tmp;
switch (GET_MODE (op0))
{
case QImode:
case HImode:
tmp = m68hc11_expand_compare (code, op0, op1);
tmp = gen_rtx_IF_THEN_ELSE (VOIDmode, tmp,
gen_rtx_LABEL_REF (VOIDmode, label),
pc_rtx);
emit_jump_insn (gen_rtx_SET (VOIDmode, pc_rtx, tmp));
return 0;
#if 0
case SFmode:
case DFmode:
{
rtvec vec;
int use_fcomi;
code = m68hc11_prepare_fp_compare_args (code, &m68hc11_compare_op0,
&m68hc11_compare_op1);
tmp = gen_rtx_fmt_ee (code, m68hc11_fp_compare_mode (code),
m68hc11_compare_op0, m68hc11_compare_op1);
tmp = gen_rtx_IF_THEN_ELSE (VOIDmode, tmp,
gen_rtx_LABEL_REF (VOIDmode, label),
pc_rtx);
tmp = gen_rtx_SET (VOIDmode, pc_rtx, tmp);
use_fcomi = ix86_use_fcomi_compare (code);
vec = rtvec_alloc (3 + !use_fcomi);
RTVEC_ELT (vec, 0) = tmp;
RTVEC_ELT (vec, 1)
= gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (CCFPmode, 18));
RTVEC_ELT (vec, 2)
= gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (CCFPmode, 17));
if (!use_fcomi)
RTVEC_ELT (vec, 3)
= gen_rtx_CLOBBER (VOIDmode, gen_rtx_SCRATCH (HImode));
emit_jump_insn (gen_rtx_PARALLEL (VOIDmode, vec));
return;
}
#endif
case SImode:
{
rtx lo[2], hi[2], label2;
enum rtx_code code1, code2, code3;
if (CONSTANT_P (op0) && !CONSTANT_P (op1))
{
tmp = op0;
op0 = op1;
op1 = tmp;
code = swap_condition (code);
}
lo[0] = m68hc11_gen_lowpart (HImode, op0);
lo[1] = m68hc11_gen_lowpart (HImode, op1);
hi[0] = m68hc11_gen_highpart (HImode, op0);
hi[1] = m68hc11_gen_highpart (HImode, op1);
if (GET_CODE (hi[1]) == CONST_INT && lo[1] == const0_rtx
&& (code == LT || code == LTU))
{
return m68hc11_expand_compare_and_branch (code, hi[0], hi[1],
label);
}
label2 = gen_label_rtx ();
code1 = code;
code2 = swap_condition (code);
code3 = unsigned_condition (code);
switch (code)
{
case LT:
case GT:
case LTU:
case GTU:
break;
case LE:
code1 = LT;
code2 = GT;
break;
case GE:
code1 = GT;
code2 = LT;
break;
case LEU:
code1 = LTU;
code2 = GTU;
break;
case GEU:
code1 = GTU;
code2 = LTU;
break;
case EQ:
code1 = UNKNOWN;
code2 = NE;
break;
case NE:
code2 = UNKNOWN;
break;
default:
abort ();
}
if (code1 != UNKNOWN)
m68hc11_expand_compare_and_branch (code1, hi[0], hi[1], label);
if (code2 != UNKNOWN)
m68hc11_expand_compare_and_branch (code2, hi[0], hi[1], label2);
m68hc11_expand_compare_and_branch (code3, lo[0], lo[1], label);
if (code2 != UNKNOWN)
emit_label (label2);
return 0;
}
default:
abort ();
}
return 0;
}
static int
autoinc_mode (rtx x)
{
if (GET_CODE (x) != MEM)
return CONST;
x = XEXP (x, 0);
if (GET_CODE (x) == PRE_INC
|| GET_CODE (x) == PRE_DEC
|| GET_CODE (x) == POST_INC
|| GET_CODE (x) == POST_DEC)
return GET_CODE (x);
return CONST;
}
static int
m68hc11_make_autoinc_notes (rtx *x, void *data)
{
rtx insn;
switch (GET_CODE (*x))
{
case PRE_DEC:
case PRE_INC:
case POST_DEC:
case POST_INC:
insn = (rtx) data;
REG_NOTES (insn) = alloc_EXPR_LIST (REG_INC, XEXP (*x, 0),
REG_NOTES (insn));
return -1;
default:
return 0;
}
}
void
m68hc11_split_move (rtx to, rtx from, rtx scratch)
{
rtx low_to, low_from;
rtx high_to, high_from;
rtx insn;
enum machine_mode mode;
int offset = 0;
int autoinc_from = autoinc_mode (from);
int autoinc_to = autoinc_mode (to);
mode = GET_MODE (to);
if (TARGET_M6812 && GET_MODE_SIZE (mode) > 2)
{
rtx reg;
int code;
if (autoinc_from == PRE_INC || autoinc_from == POST_DEC)
{
code = GET_CODE (XEXP (from, 0));
reg = XEXP (XEXP (from, 0), 0);
offset = GET_MODE_SIZE (GET_MODE (from));
if (code == POST_DEC)
offset = -offset;
if (code == PRE_INC)
emit_insn (gen_addhi3 (reg, reg, GEN_INT (offset)));
m68hc11_split_move (to, gen_rtx_MEM (GET_MODE (from), reg), scratch);
if (code == POST_DEC)
emit_insn (gen_addhi3 (reg, reg, GEN_INT (offset)));
return;
}
if (autoinc_to == PRE_INC || autoinc_to == POST_DEC)
{
code = GET_CODE (XEXP (to, 0));
reg = XEXP (XEXP (to, 0), 0);
offset = GET_MODE_SIZE (GET_MODE (to));
if (code == POST_DEC)
offset = -offset;
if (code == PRE_INC)
emit_insn (gen_addhi3 (reg, reg, GEN_INT (offset)));
m68hc11_split_move (gen_rtx_MEM (GET_MODE (to), reg), from, scratch);
if (code == POST_DEC)
emit_insn (gen_addhi3 (reg, reg, GEN_INT (offset)));
return;
}
if ((autoinc_to != autoinc_from
&& autoinc_to != CONST && autoinc_from != CONST)
|| (autoinc_to != CONST
&& reg_mentioned_p (XEXP (XEXP (to, 0), 0), from)
&& !IS_STACK_PUSH (to)))
{
code = GET_CODE (XEXP (to, 0));
reg = XEXP (XEXP (to, 0), 0);
offset = GET_MODE_SIZE (GET_MODE (to));
if (code == PRE_DEC || code == POST_DEC)
offset = -offset;
if (code == PRE_DEC || code == PRE_INC)
emit_insn (gen_addhi3 (reg, reg, GEN_INT (offset)));
m68hc11_split_move (gen_rtx_MEM (GET_MODE (to), reg), from, scratch);
if (code == POST_DEC || code == POST_INC)
emit_insn (gen_addhi3 (reg, reg, GEN_INT (offset)));
return;
}
if (autoinc_from != CONST
&& reg_mentioned_p (XEXP (XEXP (from, 0), 0), to)
&& !IS_STACK_PUSH (to))
{
code = GET_CODE (XEXP (from, 0));
reg = XEXP (XEXP (from, 0), 0);
offset = GET_MODE_SIZE (GET_MODE (from));
if (code == PRE_DEC || code == POST_DEC)
offset = -offset;
if (code == PRE_DEC || code == PRE_INC)
emit_insn (gen_addhi3 (reg, reg, GEN_INT (offset)));
m68hc11_split_move (to, gen_rtx_MEM (GET_MODE (from), reg), scratch);
if (code == POST_DEC || code == POST_INC)
emit_insn (gen_addhi3 (reg, reg, GEN_INT (offset)));
return;
}
}
if (GET_MODE_SIZE (mode) == 8)
mode = SImode;
else if (GET_MODE_SIZE (mode) == 4)
mode = HImode;
else
mode = QImode;
if (TARGET_M6812
&& IS_STACK_PUSH (to)
&& reg_mentioned_p (gen_rtx_REG (HImode, HARD_SP_REGNUM), from))
{
if (mode == SImode)
{
offset = 4;
}
else if (mode == HImode)
{
offset = 2;
}
else
offset = 0;
}
low_to = m68hc11_gen_lowpart (mode, to);
high_to = m68hc11_gen_highpart (mode, to);
low_from = m68hc11_gen_lowpart (mode, from);
high_from = m68hc11_gen_highpart (mode, from);
if (offset)
{
high_from = adjust_address (high_from, mode, offset);
low_from = high_from;
}
if (TARGET_M6812
&& GET_MODE_SIZE (mode) >= 2
&& autoinc_from != autoinc_to
&& (autoinc_from == POST_INC || autoinc_to == POST_INC))
{
rtx swap;
swap = low_to;
low_to = high_to;
high_to = swap;
swap = low_from;
low_from = high_from;
high_from = swap;
}
if (mode == SImode)
{
m68hc11_split_move (low_to, low_from, scratch);
m68hc11_split_move (high_to, high_from, scratch);
}
else if (H_REG_P (to) || H_REG_P (from)
|| (low_from == const0_rtx
&& high_from == const0_rtx
&& ! push_operand (to, GET_MODE (to))
&& ! H_REG_P (scratch))
|| (TARGET_M6812
&& (!m68hc11_register_indirect_p (from, GET_MODE (from))
|| m68hc11_small_indexed_indirect_p (from,
GET_MODE (from)))
&& (!m68hc11_register_indirect_p (to, GET_MODE (to))
|| m68hc11_small_indexed_indirect_p (to, GET_MODE (to)))))
{
insn = emit_move_insn (low_to, low_from);
for_each_rtx (&PATTERN (insn), m68hc11_make_autoinc_notes, insn);
insn = emit_move_insn (high_to, high_from);
for_each_rtx (&PATTERN (insn), m68hc11_make_autoinc_notes, insn);
}
else
{
insn = emit_move_insn (scratch, low_from);
for_each_rtx (&PATTERN (insn), m68hc11_make_autoinc_notes, insn);
insn = emit_move_insn (low_to, scratch);
for_each_rtx (&PATTERN (insn), m68hc11_make_autoinc_notes, insn);
insn = emit_move_insn (scratch, high_from);
for_each_rtx (&PATTERN (insn), m68hc11_make_autoinc_notes, insn);
insn = emit_move_insn (high_to, scratch);
for_each_rtx (&PATTERN (insn), m68hc11_make_autoinc_notes, insn);
}
}
static rtx
simplify_logical (enum machine_mode mode, int code, rtx operand, rtx *result)
{
int val;
int mask;
*result = 0;
if (GET_CODE (operand) != CONST_INT)
return operand;
if (mode == HImode)
mask = 0x0ffff;
else
mask = 0x0ff;
val = INTVAL (operand);
switch (code)
{
case IOR:
if ((val & mask) == 0)
return 0;
if ((val & mask) == mask)
*result = constm1_rtx;
break;
case AND:
if ((val & mask) == 0)
*result = const0_rtx;
if ((val & mask) == mask)
return 0;
break;
case XOR:
if ((val & mask) == 0)
return 0;
break;
}
return operand;
}
static void
m68hc11_emit_logical (enum machine_mode mode, int code, rtx *operands)
{
rtx result;
int need_copy;
need_copy = (rtx_equal_p (operands[0], operands[1])
|| rtx_equal_p (operands[0], operands[2])) ? 0 : 1;
operands[1] = simplify_logical (mode, code, operands[1], &result);
operands[2] = simplify_logical (mode, code, operands[2], &result);
if (result && GET_CODE (result) == CONST_INT)
{
if (!H_REG_P (operands[0]) && operands[3]
&& (INTVAL (result) != 0 || IS_STACK_PUSH (operands[0])))
{
emit_move_insn (operands[3], result);
emit_move_insn (operands[0], operands[3]);
}
else
{
emit_move_insn (operands[0], result);
}
}
else if (operands[1] != 0 && operands[2] != 0)
{
rtx insn;
if (!H_REG_P (operands[0]) && operands[3])
{
emit_move_insn (operands[3], operands[1]);
emit_insn (gen_rtx_SET (mode,
operands[3],
gen_rtx_fmt_ee (code, mode,
operands[3], operands[2])));
insn = emit_move_insn (operands[0], operands[3]);
}
else
{
insn = emit_insn (gen_rtx_SET (mode,
operands[0],
gen_rtx_fmt_ee (code, mode,
operands[0],
operands[2])));
}
}
else if (need_copy)
{
rtx src;
if (GET_CODE (operands[1]) == CONST_INT)
src = operands[2];
else
src = operands[1];
if (!H_REG_P (operands[0]) && !H_REG_P (src))
{
emit_move_insn (operands[3], src);
emit_move_insn (operands[0], operands[3]);
}
else
{
emit_move_insn (operands[0], src);
}
}
}
void
m68hc11_split_logical (enum machine_mode mode, int code, rtx *operands)
{
rtx low[4];
rtx high[4];
low[0] = m68hc11_gen_lowpart (mode, operands[0]);
low[1] = m68hc11_gen_lowpart (mode, operands[1]);
low[2] = m68hc11_gen_lowpart (mode, operands[2]);
high[0] = m68hc11_gen_highpart (mode, operands[0]);
high[1] = m68hc11_gen_highpart (mode, operands[1]);
high[2] = m68hc11_gen_highpart (mode, operands[2]);
low[3] = operands[3];
high[3] = operands[3];
if (mode == SImode)
{
m68hc11_split_logical (HImode, code, low);
m68hc11_split_logical (HImode, code, high);
return;
}
m68hc11_emit_logical (mode, code, low);
m68hc11_emit_logical (mode, code, high);
}
void
m68hc11_output_swap (rtx insn ATTRIBUTE_UNUSED, rtx operands[])
{
if (X_REG_P (operands[1]) || X_REG_P (operands[0]))
{
if (cc_prev_status.value1 != 0
&& (D_REG_P (cc_prev_status.value1)
|| X_REG_P (cc_prev_status.value1)))
{
cc_status = cc_prev_status;
if (D_REG_P (cc_status.value1))
cc_status.value1 = gen_rtx_REG (GET_MODE (cc_status.value1),
HARD_X_REGNUM);
else
cc_status.value1 = gen_rtx_REG (GET_MODE (cc_status.value1),
HARD_D_REGNUM);
}
else
CC_STATUS_INIT;
output_asm_insn ("xgdx", operands);
}
else
{
if (cc_prev_status.value1 != 0
&& (D_REG_P (cc_prev_status.value1)
|| Y_REG_P (cc_prev_status.value1)))
{
cc_status = cc_prev_status;
if (D_REG_P (cc_status.value1))
cc_status.value1 = gen_rtx_REG (GET_MODE (cc_status.value1),
HARD_Y_REGNUM);
else
cc_status.value1 = gen_rtx_REG (GET_MODE (cc_status.value1),
HARD_D_REGNUM);
}
else
CC_STATUS_INIT;
output_asm_insn ("xgdy", operands);
}
}
int
next_insn_test_reg (rtx insn, rtx reg)
{
rtx body;
insn = next_nonnote_insn (insn);
if (GET_CODE (insn) != INSN)
return 0;
body = PATTERN (insn);
if (sets_cc0_p (body) != 1)
return 0;
if (rtx_equal_p (XEXP (body, 1), reg) == 0)
return 0;
return 1;
}
void
m68hc11_gen_movhi (rtx insn, rtx *operands)
{
int reg;
if (operands[0] == operands[1] || rtx_equal_p (operands[0], operands[1]))
{
cc_status = cc_prev_status;
return;
}
if (TARGET_M6812)
{
rtx from = operands[1];
rtx to = operands[0];
if (IS_STACK_PUSH (to) && H_REG_P (from))
{
cc_status = cc_prev_status;
switch (REGNO (from))
{
case HARD_X_REGNUM:
case HARD_Y_REGNUM:
case HARD_D_REGNUM:
output_asm_insn ("psh%1", operands);
break;
case HARD_SP_REGNUM:
output_asm_insn ("sts\t2,-sp", operands);
break;
default:
abort ();
}
return;
}
if (IS_STACK_POP (from) && H_REG_P (to))
{
cc_status = cc_prev_status;
switch (REGNO (to))
{
case HARD_X_REGNUM:
case HARD_Y_REGNUM:
case HARD_D_REGNUM:
output_asm_insn ("pul%0", operands);
break;
default:
abort ();
}
return;
}
if (H_REG_P (operands[0]) && H_REG_P (operands[1]))
{
m68hc11_notice_keep_cc (operands[0]);
output_asm_insn ("tfr\t%1,%0", operands);
}
else if (H_REG_P (operands[0]))
{
if (SP_REG_P (operands[0]))
output_asm_insn ("lds\t%1", operands);
else
output_asm_insn ("ld%0\t%1", operands);
}
else if (H_REG_P (operands[1]))
{
if (SP_REG_P (operands[1]))
output_asm_insn ("sts\t%0", operands);
else
output_asm_insn ("st%1\t%0", operands);
}
else if (GET_CODE (to) == MEM && GET_CODE (XEXP (to, 0)) == MEM)
{
rtx ops[4];
ops[0] = to;
ops[2] = from;
ops[3] = 0;
if (dead_register_here (insn, d_reg))
ops[1] = d_reg;
else if (dead_register_here (insn, ix_reg))
ops[1] = ix_reg;
else if (dead_register_here (insn, iy_reg))
ops[1] = iy_reg;
else
{
ops[1] = d_reg;
ops[3] = d_reg;
output_asm_insn ("psh%3", ops);
}
ops[0] = to;
ops[2] = from;
output_asm_insn ("ld%1\t%2", ops);
output_asm_insn ("st%1\t%0", ops);
if (ops[3])
output_asm_insn ("pul%3", ops);
}
else if (GET_CODE (from) == CONST_INT
&& INTVAL (from) == 0
&& (MEM_VOLATILE_P (to) == 0
|| m68hc11_small_indexed_indirect_p (to, HImode) == 0))
{
output_asm_insn ("clr\t%h0", operands);
output_asm_insn ("clr\t%b0", operands);
}
else
{
if ((m68hc11_register_indirect_p (from, GET_MODE (from))
&& !m68hc11_small_indexed_indirect_p (from, GET_MODE (from)))
|| (m68hc11_register_indirect_p (to, GET_MODE (to))
&& !m68hc11_small_indexed_indirect_p (to, GET_MODE (to))))
{
rtx ops[3];
if (operands[2])
{
ops[0] = operands[2];
ops[1] = from;
ops[2] = 0;
m68hc11_gen_movhi (insn, ops);
ops[0] = to;
ops[1] = operands[2];
m68hc11_gen_movhi (insn, ops);
return;
}
else
{
fatal_insn ("move insn not handled", insn);
}
}
else
{
m68hc11_notice_keep_cc (operands[0]);
output_asm_insn ("movw\t%1,%0", operands);
}
}
return;
}
if (IS_STACK_POP (operands[1]) && H_REG_P (operands[0]))
{
cc_status = cc_prev_status;
switch (REGNO (operands[0]))
{
case HARD_X_REGNUM:
case HARD_Y_REGNUM:
output_asm_insn ("pul%0", operands);
break;
case HARD_D_REGNUM:
output_asm_insn ("pula", operands);
output_asm_insn ("pulb", operands);
break;
default:
abort ();
}
return;
}
if (H_REG_P (operands[0]))
{
switch (REGNO (operands[0]))
{
case HARD_D_REGNUM:
if (X_REG_P (operands[1]))
{
if (optimize && find_regno_note (insn, REG_DEAD, HARD_X_REGNUM))
{
m68hc11_output_swap (insn, operands);
}
else if (next_insn_test_reg (insn, operands[0]))
{
output_asm_insn ("stx\t%t0\n\tldd\t%t0", operands);
}
else
{
m68hc11_notice_keep_cc (operands[0]);
output_asm_insn ("pshx\n\tpula\n\tpulb", operands);
}
}
else if (Y_REG_P (operands[1]))
{
if (optimize && find_regno_note (insn, REG_DEAD, HARD_Y_REGNUM))
{
m68hc11_output_swap (insn, operands);
}
else
{
output_asm_insn ("sty\t%t1", operands);
output_asm_insn ("ldd\t%t1", operands);
}
}
else if (SP_REG_P (operands[1]))
{
CC_STATUS_INIT;
if (ix_reg == 0)
create_regs_rtx ();
if (optimize == 0 || dead_register_here (insn, ix_reg) == 0)
output_asm_insn ("xgdx", operands);
output_asm_insn ("tsx", operands);
output_asm_insn ("xgdx", operands);
}
else if (IS_STACK_POP (operands[1]))
{
output_asm_insn ("pula\n\tpulb", operands);
}
else if (GET_CODE (operands[1]) == CONST_INT
&& INTVAL (operands[1]) == 0)
{
output_asm_insn ("clra\n\tclrb", operands);
}
else
{
output_asm_insn ("ldd\t%1", operands);
}
break;
case HARD_X_REGNUM:
if (D_REG_P (operands[1]))
{
if (optimize && find_regno_note (insn, REG_DEAD, HARD_D_REGNUM))
{
m68hc11_output_swap (insn, operands);
}
else if (next_insn_test_reg (insn, operands[0]))
{
output_asm_insn ("std\t%t0\n\tldx\t%t0", operands);
}
else
{
m68hc11_notice_keep_cc (operands[0]);
output_asm_insn ("pshb", operands);
output_asm_insn ("psha", operands);
output_asm_insn ("pulx", operands);
}
}
else if (Y_REG_P (operands[1]))
{
if (optimize && find_regno_note (insn, REG_DEAD, HARD_Y_REGNUM)
&& dead_register_here (insn, d_reg))
{
output_asm_insn ("xgdy", operands);
output_asm_insn ("xgdx", operands);
CC_STATUS_INIT;
}
else if (!optimize_size)
{
output_asm_insn ("sty\t%t1", operands);
output_asm_insn ("ldx\t%t1", operands);
}
else
{
CC_STATUS_INIT;
output_asm_insn ("pshy", operands);
output_asm_insn ("pulx", operands);
}
}
else if (SP_REG_P (operands[1]))
{
cc_status = cc_prev_status;
output_asm_insn ("tsx", operands);
}
else
{
output_asm_insn ("ldx\t%1", operands);
}
break;
case HARD_Y_REGNUM:
if (D_REG_P (operands[1]))
{
if (optimize && find_regno_note (insn, REG_DEAD, HARD_D_REGNUM))
{
m68hc11_output_swap (insn, operands);
}
else
{
output_asm_insn ("std\t%t1", operands);
output_asm_insn ("ldy\t%t1", operands);
}
}
else if (X_REG_P (operands[1]))
{
if (optimize && find_regno_note (insn, REG_DEAD, HARD_X_REGNUM)
&& dead_register_here (insn, d_reg))
{
output_asm_insn ("xgdx", operands);
output_asm_insn ("xgdy", operands);
CC_STATUS_INIT;
}
else if (!optimize_size)
{
output_asm_insn ("stx\t%t1", operands);
output_asm_insn ("ldy\t%t1", operands);
}
else
{
CC_STATUS_INIT;
output_asm_insn ("pshx", operands);
output_asm_insn ("puly", operands);
}
}
else if (SP_REG_P (operands[1]))
{
cc_status = cc_prev_status;
output_asm_insn ("tsy", operands);
}
else
{
output_asm_insn ("ldy\t%1", operands);
}
break;
case HARD_SP_REGNUM:
if (D_REG_P (operands[1]))
{
m68hc11_notice_keep_cc (operands[0]);
output_asm_insn ("xgdx", operands);
output_asm_insn ("txs", operands);
output_asm_insn ("xgdx", operands);
}
else if (X_REG_P (operands[1]))
{
cc_status = cc_prev_status;
output_asm_insn ("txs", operands);
}
else if (Y_REG_P (operands[1]))
{
cc_status = cc_prev_status;
output_asm_insn ("tys", operands);
}
else
{
CC_STATUS_INIT;
output_asm_insn ("lds\t%1", operands);
output_asm_insn ("des", operands);
}
break;
default:
fatal_insn ("invalid register in the move instruction", insn);
break;
}
return;
}
if (SP_REG_P (operands[1]) && REG_P (operands[0])
&& REGNO (operands[0]) == HARD_FRAME_POINTER_REGNUM)
{
output_asm_insn ("sts\t%0", operands);
return;
}
if (IS_STACK_PUSH (operands[0]) && H_REG_P (operands[1]))
{
cc_status = cc_prev_status;
switch (REGNO (operands[1]))
{
case HARD_X_REGNUM:
case HARD_Y_REGNUM:
output_asm_insn ("psh%1", operands);
break;
case HARD_D_REGNUM:
output_asm_insn ("pshb", operands);
output_asm_insn ("psha", operands);
break;
default:
abort ();
}
return;
}
if (!H_REG_P (operands[1]))
{
fatal_insn ("invalid operand in the instruction", insn);
}
reg = REGNO (operands[1]);
switch (reg)
{
case HARD_D_REGNUM:
output_asm_insn ("std\t%0", operands);
break;
case HARD_X_REGNUM:
output_asm_insn ("stx\t%0", operands);
break;
case HARD_Y_REGNUM:
output_asm_insn ("sty\t%0", operands);
break;
case HARD_SP_REGNUM:
if (ix_reg == 0)
create_regs_rtx ();
if (REG_P (operands[0]) && REGNO (operands[0]) == SOFT_TMP_REGNUM)
{
output_asm_insn ("pshx", operands);
output_asm_insn ("tsx", operands);
output_asm_insn ("inx", operands);
output_asm_insn ("inx", operands);
output_asm_insn ("stx\t%0", operands);
output_asm_insn ("pulx", operands);
}
else if (reg_mentioned_p (ix_reg, operands[0]))
{
output_asm_insn ("sty\t%t0", operands);
output_asm_insn ("tsy", operands);
output_asm_insn ("sty\t%0", operands);
output_asm_insn ("ldy\t%t0", operands);
}
else
{
output_asm_insn ("stx\t%t0", operands);
output_asm_insn ("tsx", operands);
output_asm_insn ("stx\t%0", operands);
output_asm_insn ("ldx\t%t0", operands);
}
CC_STATUS_INIT;
break;
default:
fatal_insn ("invalid register in the move instruction", insn);
break;
}
}
void
m68hc11_gen_movqi (rtx insn, rtx *operands)
{
if (operands[0] == operands[1] || rtx_equal_p (operands[0], operands[1]))
{
cc_status = cc_prev_status;
return;
}
if (TARGET_M6812)
{
if (H_REG_P (operands[0]) && H_REG_P (operands[1]))
{
m68hc11_notice_keep_cc (operands[0]);
output_asm_insn ("tfr\t%1,%0", operands);
}
else if (H_REG_P (operands[0]))
{
if (IS_STACK_POP (operands[1]))
output_asm_insn ("pul%b0", operands);
else if (Q_REG_P (operands[0]))
output_asm_insn ("lda%0\t%b1", operands);
else if (D_REG_P (operands[0]))
output_asm_insn ("ldab\t%b1", operands);
else
goto m6811_move;
}
else if (H_REG_P (operands[1]))
{
if (Q_REG_P (operands[1]))
output_asm_insn ("sta%1\t%b0", operands);
else if (D_REG_P (operands[1]))
output_asm_insn ("stab\t%b0", operands);
else
goto m6811_move;
}
else
{
rtx from = operands[1];
rtx to = operands[0];
if ((m68hc11_register_indirect_p (from, GET_MODE (from))
&& !m68hc11_small_indexed_indirect_p (from, GET_MODE (from)))
|| (m68hc11_register_indirect_p (to, GET_MODE (to))
&& !m68hc11_small_indexed_indirect_p (to, GET_MODE (to))))
{
rtx ops[3];
if (operands[2])
{
ops[0] = operands[2];
ops[1] = from;
ops[2] = 0;
m68hc11_gen_movqi (insn, ops);
ops[0] = to;
ops[1] = operands[2];
m68hc11_gen_movqi (insn, ops);
}
else
{
fatal_insn ("move insn not handled", insn);
}
}
else
{
if (GET_CODE (from) == CONST_INT && INTVAL (from) == 0)
{
output_asm_insn ("clr\t%b0", operands);
}
else
{
m68hc11_notice_keep_cc (operands[0]);
output_asm_insn ("movb\t%b1,%b0", operands);
}
}
}
return;
}
m6811_move:
if (H_REG_P (operands[0]))
{
switch (REGNO (operands[0]))
{
case HARD_B_REGNUM:
case HARD_D_REGNUM:
if (X_REG_P (operands[1]))
{
if (optimize && find_regno_note (insn, REG_DEAD, HARD_X_REGNUM))
{
m68hc11_output_swap (insn, operands);
}
else
{
output_asm_insn ("stx\t%t1", operands);
output_asm_insn ("ldab\t%T0", operands);
}
}
else if (Y_REG_P (operands[1]))
{
if (optimize && find_regno_note (insn, REG_DEAD, HARD_Y_REGNUM))
{
m68hc11_output_swap (insn, operands);
}
else
{
output_asm_insn ("sty\t%t1", operands);
output_asm_insn ("ldab\t%T0", operands);
}
}
else if (!DB_REG_P (operands[1]) && !D_REG_P (operands[1])
&& !DA_REG_P (operands[1]))
{
output_asm_insn ("ldab\t%b1", operands);
}
else if (DA_REG_P (operands[1]))
{
output_asm_insn ("tab", operands);
}
else
{
cc_status = cc_prev_status;
return;
}
break;
case HARD_A_REGNUM:
if (X_REG_P (operands[1]))
{
output_asm_insn ("stx\t%t1", operands);
output_asm_insn ("ldaa\t%T0", operands);
}
else if (Y_REG_P (operands[1]))
{
output_asm_insn ("sty\t%t1", operands);
output_asm_insn ("ldaa\t%T0", operands);
}
else if (!DB_REG_P (operands[1]) && !D_REG_P (operands[1])
&& !DA_REG_P (operands[1]))
{
output_asm_insn ("ldaa\t%b1", operands);
}
else if (!DA_REG_P (operands[1]))
{
output_asm_insn ("tba", operands);
}
else
{
cc_status = cc_prev_status;
}
break;
case HARD_X_REGNUM:
if (D_REG_P (operands[1]))
{
if (optimize && find_regno_note (insn, REG_DEAD, HARD_D_REGNUM))
{
m68hc11_output_swap (insn, operands);
}
else
{
output_asm_insn ("stab\t%T1", operands);
output_asm_insn ("ldx\t%t1", operands);
}
CC_STATUS_INIT;
}
else if (Y_REG_P (operands[1]))
{
output_asm_insn ("sty\t%t0", operands);
output_asm_insn ("ldx\t%t0", operands);
}
else if (GET_CODE (operands[1]) == CONST_INT)
{
output_asm_insn ("ldx\t%1", operands);
}
else if (dead_register_here (insn, d_reg))
{
output_asm_insn ("ldab\t%b1", operands);
output_asm_insn ("xgdx", operands);
}
else if (!reg_mentioned_p (operands[0], operands[1]))
{
output_asm_insn ("xgdx", operands);
output_asm_insn ("ldab\t%b1", operands);
output_asm_insn ("xgdx", operands);
}
else
{
output_asm_insn ("pshb", operands);
output_asm_insn ("ldab\t%b1", operands);
output_asm_insn ("stab\t%T1", operands);
output_asm_insn ("ldx\t%t1", operands);
output_asm_insn ("pulb", operands);
CC_STATUS_INIT;
}
break;
case HARD_Y_REGNUM:
if (D_REG_P (operands[1]))
{
output_asm_insn ("stab\t%T1", operands);
output_asm_insn ("ldy\t%t1", operands);
CC_STATUS_INIT;
}
else if (X_REG_P (operands[1]))
{
output_asm_insn ("stx\t%t1", operands);
output_asm_insn ("ldy\t%t1", operands);
CC_STATUS_INIT;
}
else if (GET_CODE (operands[1]) == CONST_INT)
{
output_asm_insn ("ldy\t%1", operands);
}
else if (dead_register_here (insn, d_reg))
{
output_asm_insn ("ldab\t%b1", operands);
output_asm_insn ("xgdy", operands);
}
else if (!reg_mentioned_p (operands[0], operands[1]))
{
output_asm_insn ("xgdy", operands);
output_asm_insn ("ldab\t%b1", operands);
output_asm_insn ("xgdy", operands);
}
else
{
output_asm_insn ("pshb", operands);
output_asm_insn ("ldab\t%b1", operands);
output_asm_insn ("stab\t%T1", operands);
output_asm_insn ("ldy\t%t1", operands);
output_asm_insn ("pulb", operands);
CC_STATUS_INIT;
}
break;
default:
fatal_insn ("invalid register in the instruction", insn);
break;
}
}
else if (H_REG_P (operands[1]))
{
switch (REGNO (operands[1]))
{
case HARD_D_REGNUM:
case HARD_B_REGNUM:
output_asm_insn ("stab\t%b0", operands);
break;
case HARD_A_REGNUM:
output_asm_insn ("staa\t%b0", operands);
break;
case HARD_X_REGNUM:
output_asm_insn ("xgdx\n\tstab\t%b0\n\txgdx", operands);
break;
case HARD_Y_REGNUM:
output_asm_insn ("xgdy\n\tstab\t%b0\n\txgdy", operands);
break;
default:
fatal_insn ("invalid register in the move instruction", insn);
break;
}
return;
}
else
{
fatal_insn ("operand 1 must be a hard register", insn);
}
}
void
m68hc11_gen_rotate (enum rtx_code code, rtx insn, rtx operands[])
{
int val;
if (GET_CODE (operands[2]) != CONST_INT
|| (!D_REG_P (operands[0]) && !DA_REG_P (operands[0])))
fatal_insn ("invalid rotate insn", insn);
val = INTVAL (operands[2]);
if (code == ROTATERT)
val = GET_MODE_SIZE (GET_MODE (operands[0])) * BITS_PER_UNIT - val;
if (GET_MODE (operands[0]) != QImode)
CC_STATUS_INIT;
if (val >= 5 && val <= 11)
{
if (TARGET_M6812)
output_asm_insn ("exg\ta,b", operands);
else
{
output_asm_insn ("psha", operands);
output_asm_insn ("tba", operands);
output_asm_insn ("pulb", operands);
}
val -= 8;
}
else if (val >= 12)
{
val = val - 16;
}
if (val > 0)
{
while (--val >= 0)
{
if (GET_MODE (operands[0]) != QImode)
{
output_asm_insn ("asra", operands);
output_asm_insn ("rola", operands);
}
if (D_REG_P (operands[0]))
output_asm_insn ("rolb", operands);
if (GET_MODE (operands[0]) != QImode || DA_REG_P (operands[0]))
output_asm_insn ("rola", operands);
}
}
else
{
while (++val <= 0)
{
if (GET_MODE (operands[0]) != QImode)
output_asm_insn ("tap", operands);
if (D_REG_P (operands[0]))
output_asm_insn ("rorb", operands);
if (GET_MODE (operands[0]) != QImode || DA_REG_P (operands[0]))
output_asm_insn ("rora", operands);
}
}
}
void
m68hc11_notice_update_cc (rtx exp, rtx insn ATTRIBUTE_UNUSED)
{
if (GET_CODE (exp) == SET)
{
if (SET_DEST (exp) == pc_rtx)
;
else if (GET_CODE (SET_SRC (exp)) == CALL)
{
CC_STATUS_INIT;
}
else if (SET_DEST (exp) == cc0_rtx)
{
cc_status.flags = 0;
cc_status.value1 = XEXP (exp, 0);
cc_status.value2 = XEXP (exp, 1);
}
else
{
cc_status.flags = 0;
cc_status.value1 = XEXP (exp, 0);
cc_status.value2 = XEXP (exp, 1);
}
}
else
{
CC_STATUS_INIT;
}
if (cc_status.value2 != 0)
switch (GET_CODE (cc_status.value2))
{
case IOR:
case XOR:
case AND:
break;
case NOT:
if (GET_MODE (cc_status.value2) != QImode)
CC_STATUS_INIT;
break;
case PLUS:
case MINUS:
case MULT:
case DIV:
case UDIV:
case MOD:
case UMOD:
case NEG:
if (GET_MODE (cc_status.value2) != VOIDmode)
cc_status.flags |= CC_NO_OVERFLOW;
break;
case ASHIFT:
case ROTATE:
case ROTATERT:
if (GET_MODE (cc_status.value2) != VOIDmode)
cc_status.flags |= CC_NO_OVERFLOW;
break;
case MEM:
case SYMBOL_REF:
case REG:
case CONST_INT:
cc_status.flags |= CC_NO_OVERFLOW;
break;
default:
break;
}
if (cc_status.value1 && GET_CODE (cc_status.value1) == REG
&& cc_status.value2
&& reg_overlap_mentioned_p (cc_status.value1, cc_status.value2))
cc_status.value2 = 0;
else if (cc_status.value1 && side_effects_p (cc_status.value1))
cc_status.value1 = 0;
else if (cc_status.value2 && side_effects_p (cc_status.value2))
cc_status.value2 = 0;
}
void
m68hc11_notice_keep_cc (rtx reg)
{
if (reg == 0
|| cc_prev_status.value1 == 0
|| rtx_equal_p (reg, cc_prev_status.value1)
|| (cc_prev_status.value2
&& reg_mentioned_p (reg, cc_prev_status.value2)))
CC_STATUS_INIT;
else
cc_status = cc_prev_status;
}
struct replace_info
{
rtx first;
rtx replace_reg;
int need_save_z;
int must_load_z;
int must_save_reg;
int must_restore_reg;
rtx last;
int regno;
int x_used;
int y_used;
int can_use_d;
int found_call;
int z_died;
int z_set_count;
rtx z_value;
int must_push_reg;
int save_before_last;
int z_loaded_with_sp;
};
static int m68hc11_check_z_replacement (rtx, struct replace_info *);
static void m68hc11_find_z_replacement (rtx, struct replace_info *);
static void m68hc11_z_replacement (rtx);
static void m68hc11_reassign_regs (rtx);
int z_replacement_completed = 0;
static int
m68hc11_check_z_replacement (rtx insn, struct replace_info *info)
{
int this_insn_uses_ix;
int this_insn_uses_iy;
int this_insn_uses_z;
int this_insn_uses_z_in_dst;
int this_insn_uses_d;
rtx body;
int z_dies_here;
if (GET_CODE (insn) == CALL_INSN)
{
body = PATTERN (insn);
info->can_use_d = 0;
if (reg_mentioned_p (z_reg, body))
{
insn = NEXT_INSN (insn);
info->x_used = 1;
info->y_used = 0;
info->found_call = 1;
info->must_restore_reg = 0;
info->last = NEXT_INSN (insn);
}
info->need_save_z = 0;
return 0;
}
if (GET_CODE (insn) == CODE_LABEL
|| GET_CODE (insn) == BARRIER || GET_CODE (insn) == ASM_INPUT)
return 0;
if (GET_CODE (insn) == JUMP_INSN)
{
if (reg_mentioned_p (z_reg, insn) == 0)
return 0;
info->can_use_d = 0;
info->must_save_reg = 0;
info->must_restore_reg = 0;
info->need_save_z = 0;
info->last = NEXT_INSN (insn);
return 0;
}
if (GET_CODE (insn) != INSN && GET_CODE (insn) != JUMP_INSN)
{
return 1;
}
z_dies_here = find_regno_note (insn, REG_DEAD, HARD_Z_REGNUM) != NULL;
body = PATTERN (insn);
if (GET_CODE (body) == SET)
{
rtx src = XEXP (body, 1);
rtx dst = XEXP (body, 0);
if (dst == cc0_rtx)
{
if ((GET_CODE (src) == REG && REGNO (src) == HARD_Z_REGNUM)
|| (GET_CODE (src) == COMPARE &&
((rtx_equal_p (XEXP (src, 0), z_reg)
&& H_REG_P (XEXP (src, 1)))
|| (rtx_equal_p (XEXP (src, 1), z_reg)
&& H_REG_P (XEXP (src, 0))))))
{
if (insn == info->first)
{
info->must_load_z = 0;
info->must_save_reg = 0;
info->must_restore_reg = 0;
info->need_save_z = 0;
info->found_call = 1;
info->regno = SOFT_Z_REGNUM;
info->last = NEXT_INSN (insn);
}
return 0;
}
if (reg_mentioned_p (z_reg, src) == 0)
{
info->can_use_d = 0;
return 0;
}
if (insn != info->first)
return 0;
info->must_push_reg = 1;
info->last = insn;
}
if (Z_REG_P (dst))
{
if (!reg_mentioned_p (z_reg, src))
{
if (insn != info->first)
{
return 0;
}
info->must_load_z = 0;
}
info->z_set_count++;
info->z_value = src;
if (SP_REG_P (src))
info->z_loaded_with_sp = 1;
}
else if (reg_mentioned_p (z_reg, dst))
info->can_use_d = 0;
this_insn_uses_d = reg_mentioned_p (d_reg, src)
| reg_mentioned_p (d_reg, dst);
this_insn_uses_ix = reg_mentioned_p (ix_reg, src)
| reg_mentioned_p (ix_reg, dst);
this_insn_uses_iy = reg_mentioned_p (iy_reg, src)
| reg_mentioned_p (iy_reg, dst);
this_insn_uses_z = reg_mentioned_p (z_reg, src);
if (this_insn_uses_z && !Z_REG_P (src)
&& !(m68hc11_arith_operator (src, GET_MODE (src))
&& Z_REG_P (XEXP (src, 0))
&& !reg_mentioned_p (z_reg, XEXP (src, 1))
&& insn == info->first
&& dead_register_here (insn, d_reg)))
info->can_use_d = 0;
this_insn_uses_z_in_dst = reg_mentioned_p (z_reg, dst);
if (TARGET_M6812 && !z_dies_here
&& ((this_insn_uses_z && side_effects_p (src))
|| (this_insn_uses_z_in_dst && side_effects_p (dst))))
{
info->need_save_z = 1;
info->z_set_count++;
}
this_insn_uses_z |= this_insn_uses_z_in_dst;
if (this_insn_uses_z && this_insn_uses_ix && this_insn_uses_iy)
{
fatal_insn ("registers IX, IY and Z used in the same INSN", insn);
}
if (this_insn_uses_d)
info->can_use_d = 0;
if (this_insn_uses_ix && this_insn_uses_iy)
{
return 0;
}
if (this_insn_uses_ix && X_REG_P (dst) && GET_MODE (dst) == SImode)
info->can_use_d = 0;
if (info->x_used == 0 && this_insn_uses_ix)
{
if (info->y_used)
{
if (X_REG_P (dst) && rtx_equal_p (src, z_reg))
{
if (z_dies_here)
{
info->need_save_z = 0;
info->z_died = 1;
}
info->must_save_reg = 0;
info->must_restore_reg = 0;
info->found_call = 1;
info->can_use_d = 0;
PUT_CODE (insn, NOTE);
NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED;
NOTE_SOURCE_FILE (insn) = 0;
info->last = NEXT_INSN (insn);
return 0;
}
if (X_REG_P (dst)
&& (rtx_equal_p (src, z_reg)
|| (z_dies_here && !reg_mentioned_p (ix_reg, src))))
{
if (z_dies_here)
{
info->need_save_z = 0;
info->z_died = 1;
}
info->last = NEXT_INSN (insn);
info->must_save_reg = 0;
info->must_restore_reg = 0;
}
else if (X_REG_P (dst) && reg_mentioned_p (z_reg, src)
&& !reg_mentioned_p (ix_reg, src))
{
if (z_dies_here)
{
info->z_died = 1;
info->need_save_z = 0;
}
else if (TARGET_M6812 && side_effects_p (src))
{
info->last = 0;
info->must_restore_reg = 0;
return 0;
}
else
{
info->save_before_last = 1;
}
info->must_restore_reg = 0;
info->last = NEXT_INSN (insn);
}
else if (info->can_use_d)
{
info->last = NEXT_INSN (insn);
info->x_used = 1;
}
return 0;
}
info->x_used = 1;
if (z_dies_here && !reg_mentioned_p (ix_reg, src)
&& GET_CODE (dst) == REG && REGNO (dst) == HARD_X_REGNUM)
{
info->need_save_z = 0;
info->z_died = 1;
info->last = NEXT_INSN (insn);
info->regno = HARD_X_REGNUM;
info->must_save_reg = 0;
info->must_restore_reg = 0;
return 0;
}
if (rtx_equal_p (src, z_reg) && rtx_equal_p (dst, ix_reg))
{
info->regno = HARD_X_REGNUM;
info->must_restore_reg = 0;
info->must_save_reg = 0;
return 0;
}
}
if (info->y_used == 0 && this_insn_uses_iy)
{
if (info->x_used)
{
if (Y_REG_P (dst) && rtx_equal_p (src, z_reg))
{
if (z_dies_here)
{
info->need_save_z = 0;
info->z_died = 1;
}
info->must_save_reg = 0;
info->must_restore_reg = 0;
info->found_call = 1;
info->can_use_d = 0;
PUT_CODE (insn, NOTE);
NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED;
NOTE_SOURCE_FILE (insn) = 0;
info->last = NEXT_INSN (insn);
return 0;
}
if (Y_REG_P (dst)
&& (rtx_equal_p (src, z_reg)
|| (z_dies_here && !reg_mentioned_p (iy_reg, src))))
{
if (z_dies_here)
{
info->z_died = 1;
info->need_save_z = 0;
}
info->last = NEXT_INSN (insn);
info->must_save_reg = 0;
info->must_restore_reg = 0;
}
else if (Y_REG_P (dst) && reg_mentioned_p (z_reg, src)
&& !reg_mentioned_p (iy_reg, src))
{
if (z_dies_here)
{
info->z_died = 1;
info->need_save_z = 0;
}
else if (TARGET_M6812 && side_effects_p (src))
{
info->last = 0;
info->must_restore_reg = 0;
return 0;
}
else
{
info->save_before_last = 1;
}
info->must_restore_reg = 0;
info->last = NEXT_INSN (insn);
}
else if (info->can_use_d)
{
info->last = NEXT_INSN (insn);
info->y_used = 1;
}
return 0;
}
info->y_used = 1;
if (z_dies_here && !reg_mentioned_p (iy_reg, src)
&& GET_CODE (dst) == REG && REGNO (dst) == HARD_Y_REGNUM)
{
info->need_save_z = 0;
info->z_died = 1;
info->last = NEXT_INSN (insn);
info->regno = HARD_Y_REGNUM;
info->must_save_reg = 0;
info->must_restore_reg = 0;
return 0;
}
if (rtx_equal_p (src, z_reg) && rtx_equal_p (dst, iy_reg))
{
info->regno = HARD_Y_REGNUM;
info->must_restore_reg = 0;
info->must_save_reg = 0;
return 0;
}
}
if (z_dies_here)
{
info->need_save_z = 0;
info->z_died = 1;
if (info->last == 0)
info->last = NEXT_INSN (insn);
return 0;
}
return info->last != NULL_RTX ? 0 : 1;
}
if (GET_CODE (body) == PARALLEL)
{
int i;
char ix_clobber = 0;
char iy_clobber = 0;
char z_clobber = 0;
this_insn_uses_iy = 0;
this_insn_uses_ix = 0;
this_insn_uses_z = 0;
for (i = XVECLEN (body, 0) - 1; i >= 0; i--)
{
rtx x;
int uses_ix, uses_iy, uses_z;
x = XVECEXP (body, 0, i);
if (info->can_use_d && reg_mentioned_p (d_reg, x))
info->can_use_d = 0;
uses_ix = reg_mentioned_p (ix_reg, x);
uses_iy = reg_mentioned_p (iy_reg, x);
uses_z = reg_mentioned_p (z_reg, x);
if (GET_CODE (x) == CLOBBER)
{
ix_clobber |= uses_ix;
iy_clobber |= uses_iy;
z_clobber |= uses_z;
}
else
{
this_insn_uses_ix |= uses_ix;
this_insn_uses_iy |= uses_iy;
this_insn_uses_z |= uses_z;
}
if (uses_z && GET_CODE (x) == SET)
{
rtx dst = XEXP (x, 0);
if (Z_REG_P (dst))
info->z_set_count++;
}
if (TARGET_M6812 && uses_z && side_effects_p (x))
info->need_save_z = 1;
if (z_clobber)
info->need_save_z = 0;
}
if (debug_m6811)
{
printf ("Uses X:%d Y:%d Z:%d CX:%d CY:%d CZ:%d\n",
this_insn_uses_ix, this_insn_uses_iy,
this_insn_uses_z, ix_clobber, iy_clobber, z_clobber);
debug_rtx (insn);
}
if (this_insn_uses_z)
info->can_use_d = 0;
if (z_clobber && info->first != insn)
{
info->need_save_z = 0;
info->last = insn;
return 0;
}
if (z_clobber && info->x_used == 0 && info->y_used == 0)
{
if (this_insn_uses_z == 0 && insn == info->first)
{
info->must_load_z = 0;
}
if (dead_register_here (insn, d_reg))
{
info->regno = HARD_D_REGNUM;
info->must_save_reg = 0;
info->must_restore_reg = 0;
}
else if (dead_register_here (insn, ix_reg))
{
info->regno = HARD_X_REGNUM;
info->must_save_reg = 0;
info->must_restore_reg = 0;
}
else if (dead_register_here (insn, iy_reg))
{
info->regno = HARD_Y_REGNUM;
info->must_save_reg = 0;
info->must_restore_reg = 0;
}
if (info->regno >= 0)
{
info->last = NEXT_INSN (insn);
return 0;
}
if (this_insn_uses_ix == 0)
{
info->regno = HARD_X_REGNUM;
info->must_save_reg = 1;
info->must_restore_reg = 1;
}
else if (this_insn_uses_iy == 0)
{
info->regno = HARD_Y_REGNUM;
info->must_save_reg = 1;
info->must_restore_reg = 1;
}
else
{
info->regno = HARD_D_REGNUM;
info->must_save_reg = 1;
info->must_restore_reg = 1;
}
info->last = NEXT_INSN (insn);
return 0;
}
if (((info->x_used || this_insn_uses_ix) && iy_clobber)
|| ((info->y_used || this_insn_uses_iy) && ix_clobber))
{
if (this_insn_uses_z)
{
if (info->y_used == 0 && iy_clobber)
{
info->regno = HARD_Y_REGNUM;
info->must_save_reg = 0;
info->must_restore_reg = 0;
}
if (info->first != insn
&& ((info->y_used && ix_clobber)
|| (info->x_used && iy_clobber)))
info->last = insn;
else
info->last = NEXT_INSN (insn);
info->save_before_last = 1;
}
return 0;
}
if (this_insn_uses_ix && this_insn_uses_iy)
{
if (this_insn_uses_z)
{
fatal_insn ("cannot do z-register replacement", insn);
}
return 0;
}
if (info->x_used == 0 && (this_insn_uses_ix || ix_clobber))
{
if (info->y_used)
{
return 0;
}
info->x_used = 1;
if (iy_clobber || z_clobber)
{
info->last = NEXT_INSN (insn);
info->save_before_last = 1;
return 0;
}
}
if (info->y_used == 0 && (this_insn_uses_iy || iy_clobber))
{
if (info->x_used)
{
return 0;
}
info->y_used = 1;
if (ix_clobber || z_clobber)
{
info->last = NEXT_INSN (insn);
info->save_before_last = 1;
return 0;
}
}
if (z_dies_here)
{
info->z_died = 1;
info->need_save_z = 0;
}
return 1;
}
if (GET_CODE (body) == CLOBBER)
{
if (this_insn_uses_ix && this_insn_uses_iy)
{
return 0;
}
if (info->x_used == 0 && this_insn_uses_ix)
{
if (info->y_used)
{
return 0;
}
info->x_used = 1;
}
if (info->y_used == 0 && this_insn_uses_iy)
{
if (info->x_used)
{
return 0;
}
info->y_used = 1;
}
return 1;
}
return 1;
}
static void
m68hc11_find_z_replacement (rtx insn, struct replace_info *info)
{
int reg;
info->replace_reg = NULL_RTX;
info->must_load_z = 1;
info->need_save_z = 1;
info->must_save_reg = 1;
info->must_restore_reg = 1;
info->first = insn;
info->x_used = 0;
info->y_used = 0;
info->can_use_d = TARGET_M6811 ? 1 : 0;
info->found_call = 0;
info->z_died = 0;
info->last = 0;
info->regno = -1;
info->z_set_count = 0;
info->z_value = NULL_RTX;
info->must_push_reg = 0;
info->save_before_last = 0;
info->z_loaded_with_sp = 0;
for (; insn && info->z_died == 0; insn = NEXT_INSN (insn))
{
if (m68hc11_check_z_replacement (insn, info) == 0)
break;
}
if (info->z_set_count == 1)
{
rtx p = info->first;
rtx v = 0;
if (info->x_used)
{
v = find_last_value (iy_reg, &p, insn, 1);
}
else if (info->y_used)
{
v = find_last_value (ix_reg, &p, insn, 1);
}
if (v && (v != iy_reg && v != ix_reg) && rtx_equal_p (v, info->z_value))
{
if (info->x_used)
info->regno = HARD_Y_REGNUM;
else
info->regno = HARD_X_REGNUM;
info->must_load_z = 0;
info->must_save_reg = 0;
info->must_restore_reg = 0;
info->found_call = 1;
}
}
if (info->z_set_count == 0)
info->need_save_z = 0;
if (insn == 0)
info->need_save_z = 0;
if (info->last == 0)
info->last = insn;
if (info->regno >= 0)
{
reg = info->regno;
info->replace_reg = gen_rtx_REG (HImode, reg);
}
else if (info->can_use_d)
{
reg = HARD_D_REGNUM;
info->replace_reg = d_reg;
}
else if (info->x_used)
{
reg = HARD_Y_REGNUM;
info->replace_reg = iy_reg;
}
else
{
reg = HARD_X_REGNUM;
info->replace_reg = ix_reg;
}
info->regno = reg;
if (info->must_save_reg && info->must_restore_reg)
{
if (insn && dead_register_here (insn, info->replace_reg))
{
info->must_save_reg = 0;
info->must_restore_reg = 0;
}
}
}
static void
m68hc11_z_replacement (rtx insn)
{
rtx replace_reg_qi;
rtx replace_reg;
struct replace_info info;
if (GET_CODE (insn) == INSN && GET_CODE (PATTERN (insn)) == SET)
{
rtx body = PATTERN (insn);
rtx src = XEXP (body, 1);
rtx dst = XEXP (body, 0);
if (Z_REG_P (dst) && (H_REG_P (src) && !SP_REG_P (src)))
{
XEXP (body, 0) = gen_rtx_REG (GET_MODE (dst), SOFT_Z_REGNUM);
return;
}
else if (Z_REG_P (src)
&& ((H_REG_P (dst) && !SP_REG_P (src)) || dst == cc0_rtx))
{
XEXP (body, 1) = gen_rtx_REG (GET_MODE (src), SOFT_Z_REGNUM);
return;
}
else if (D_REG_P (dst)
&& m68hc11_arith_operator (src, GET_MODE (src))
&& D_REG_P (XEXP (src, 0)) && Z_REG_P (XEXP (src, 1)))
{
XEXP (src, 1) = gen_rtx_REG (GET_MODE (src), SOFT_Z_REGNUM);
return;
}
else if (Z_REG_P (dst) && GET_CODE (src) == CONST_INT
&& INTVAL (src) == 0)
{
XEXP (body, 0) = gen_rtx_REG (GET_MODE (dst), SOFT_Z_REGNUM);
INSN_CODE (insn) = -1;
return;
}
}
m68hc11_find_z_replacement (insn, &info);
replace_reg = info.replace_reg;
replace_reg_qi = NULL_RTX;
if (info.must_save_reg && !info.must_push_reg)
{
rtx dst;
if (info.must_push_reg && 0)
dst = gen_rtx_MEM (HImode,
gen_rtx_PRE_DEC (HImode,
gen_rtx_REG (HImode, HARD_SP_REGNUM)));
else
dst = gen_rtx_REG (HImode, SOFT_SAVED_XY_REGNUM);
emit_insn_before (gen_movhi (dst,
gen_rtx_REG (HImode, info.regno)), insn);
}
if (info.must_load_z && !info.must_push_reg)
{
emit_insn_before (gen_movhi (gen_rtx_REG (HImode, info.regno),
gen_rtx_REG (HImode, SOFT_Z_REGNUM)),
insn);
}
for (; insn && insn != info.last; insn = NEXT_INSN (insn))
{
rtx body;
if (GET_CODE (insn) == CODE_LABEL || GET_CODE (insn) == BARRIER)
break;
if (GET_CODE (insn) != INSN
&& GET_CODE (insn) != CALL_INSN && GET_CODE (insn) != JUMP_INSN)
continue;
body = PATTERN (insn);
if (GET_CODE (body) == SET || GET_CODE (body) == PARALLEL
|| GET_CODE (body) == ASM_OPERANDS
|| GET_CODE (insn) == CALL_INSN || GET_CODE (insn) == JUMP_INSN)
{
rtx note;
if (debug_m6811 && reg_mentioned_p (replace_reg, body))
{
printf ("Reg mentioned here...:\n");
fflush (stdout);
debug_rtx (insn);
}
if (info.must_push_reg
&& info.z_loaded_with_sp && GET_CODE (body) == SET)
{
rtx src, dst;
src = SET_SRC (body);
dst = SET_DEST (body);
if (SP_REG_P (src) && Z_REG_P (dst))
emit_insn_after (gen_addhi3 (dst, dst, const2_rtx), insn);
}
if (!validate_replace_rtx (z_reg, replace_reg, insn))
{
INSN_CODE (insn) = -1;
if (!validate_replace_rtx (z_reg, replace_reg, insn))
fatal_insn ("cannot do z-register replacement", insn);
}
if (reg_mentioned_p (z_reg, insn))
{
if (replace_reg_qi == NULL_RTX)
replace_reg_qi = gen_rtx_REG (QImode, REGNO (replace_reg));
validate_replace_rtx (z_reg_qi, replace_reg_qi, insn);
}
for (note = REG_NOTES (insn); note; note = XEXP (note, 1))
{
if (REG_NOTE_KIND (note) == REG_INC
&& GET_CODE (XEXP (note, 0)) == REG
&& REGNO (XEXP (note, 0)) == REGNO (z_reg))
{
XEXP (note, 0) = replace_reg;
}
}
}
if (GET_CODE (insn) == CALL_INSN || GET_CODE (insn) == JUMP_INSN)
break;
}
if (insn && info.need_save_z && !info.must_push_reg)
{
rtx save_pos_insn = insn;
if (info.save_before_last)
save_pos_insn = PREV_INSN (save_pos_insn);
emit_insn_after (gen_movhi (gen_rtx_REG (HImode, SOFT_Z_REGNUM),
gen_rtx_REG (HImode, info.regno)),
PREV_INSN (save_pos_insn));
}
if (info.must_push_reg && info.last)
{
rtx new_body, body;
body = PATTERN (info.last);
new_body = gen_rtx_PARALLEL (VOIDmode,
gen_rtvec (3, body,
gen_rtx_USE (VOIDmode,
replace_reg),
gen_rtx_USE (VOIDmode,
gen_rtx_REG (HImode,
SOFT_Z_REGNUM))));
PATTERN (info.last) = new_body;
INSN_CODE (insn) = -1;
if (!validate_replace_rtx (z_reg, replace_reg, info.last))
{
fatal_insn ("invalid Z register replacement for insn", insn);
}
insn = NEXT_INSN (info.last);
}
if (insn && info.must_restore_reg && !info.must_push_reg)
{
rtx dst;
if (info.must_push_reg && 0)
dst = gen_rtx_MEM (HImode,
gen_rtx_POST_INC (HImode,
gen_rtx_REG (HImode, HARD_SP_REGNUM)));
else
dst = gen_rtx_REG (HImode, SOFT_SAVED_XY_REGNUM);
emit_insn_after (gen_movhi (gen_rtx_REG (HImode, info.regno),
dst), PREV_INSN (insn));
}
}
static void
m68hc11_reassign_regs (rtx first)
{
rtx insn;
ix_reg = gen_rtx_REG (HImode, HARD_X_REGNUM);
iy_reg = gen_rtx_REG (HImode, HARD_Y_REGNUM);
z_reg = gen_rtx_REG (HImode, HARD_Z_REGNUM);
z_reg_qi = gen_rtx_REG (QImode, HARD_Z_REGNUM);
for (insn = first; insn; insn = NEXT_INSN (insn))
{
rtx body;
if (GET_CODE (insn) == CODE_LABEL
|| GET_CODE (insn) == NOTE || GET_CODE (insn) == BARRIER)
continue;
if (!INSN_P (insn))
continue;
body = PATTERN (insn);
if (GET_CODE (body) == CLOBBER || GET_CODE (body) == USE)
continue;
if (GET_CODE (body) == CONST_INT || GET_CODE (body) == ASM_INPUT
|| GET_CODE (body) == ASM_OPERANDS
|| GET_CODE (body) == UNSPEC || GET_CODE (body) == UNSPEC_VOLATILE)
continue;
if (GET_CODE (body) == SET || GET_CODE (body) == PARALLEL
|| GET_CODE (insn) == CALL_INSN || GET_CODE (insn) == JUMP_INSN)
{
if (reg_mentioned_p (z_reg, body))
{
m68hc11_z_replacement (insn);
}
}
else
{
printf ("insn not handled by Z replacement:\n");
fflush (stdout);
debug_rtx (insn);
}
}
}
static void
m68hc11_reorg (void)
{
int split_done = 0;
rtx insn, first;
z_replacement_completed = 0;
z_reg = gen_rtx_REG (HImode, HARD_Z_REGNUM);
first = get_insns ();
unshare_all_rtl_again (first);
split_all_insns_noflow ();
split_done = 1;
z_replacement_completed = 1;
m68hc11_reassign_regs (first);
if (optimize)
compute_bb_for_insn ();
if (optimize > 0 && split_done)
{
reload_cse_regs (first);
}
if (optimize)
{
for (insn = first; insn; insn = NEXT_INSN (insn))
{
if (INSN_P (insn))
{
rtx *pnote;
pnote = ®_NOTES (insn);
while (*pnote != 0)
{
if (REG_NOTE_KIND (*pnote) == REG_DEAD)
*pnote = XEXP (*pnote, 1);
else
pnote = &XEXP (*pnote, 1);
}
}
}
life_analysis (0, PROP_REG_INFO | PROP_DEATH_NOTES);
}
z_replacement_completed = 2;
if (optimize > 0)
split_all_insns_noflow ();
{
rtx insn;
for (insn = first; insn; insn = NEXT_INSN (insn))
{
rtx body;
if (INSN_DELETED_P (insn))
continue;
if (!INSN_P (insn))
continue;
body = PATTERN (insn);
if (GET_CODE (body) == SET
&& rtx_equal_p (SET_SRC (body), SET_DEST (body)))
{
PUT_CODE (insn, NOTE);
NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED;
NOTE_SOURCE_FILE (insn) = 0;
continue;
}
}
}
}
static void
m68hc11_init_libfuncs (void)
{
memcpy_libfunc = init_one_libfunc ("__memcpy");
memcmp_libfunc = init_one_libfunc ("__memcmp");
memset_libfunc = init_one_libfunc ("__memset");
}
int
m68hc11_memory_move_cost (enum machine_mode mode, enum reg_class class,
int in ATTRIBUTE_UNUSED)
{
if (class <= H_REGS && class > NO_REGS)
{
if (GET_MODE_SIZE (mode) <= 2)
return COSTS_N_INSNS (1) + (reload_completed | reload_in_progress);
else
return COSTS_N_INSNS (2) + (reload_completed | reload_in_progress);
}
else
{
if (GET_MODE_SIZE (mode) <= 2)
return COSTS_N_INSNS (3);
else
return COSTS_N_INSNS (4);
}
}
int
m68hc11_register_move_cost (enum machine_mode mode, enum reg_class from,
enum reg_class to)
{
if (from < to)
{
enum reg_class tmp = to;
to = from, from = tmp;
}
if (to >= S_REGS)
return m68hc11_memory_move_cost (mode, S_REGS, 0);
else if (from <= S_REGS)
return COSTS_N_INSNS (1) + (reload_completed | reload_in_progress);
else
return COSTS_N_INSNS (2);
}
static int
m68hc11_address_cost (rtx addr)
{
int cost = 4;
switch (GET_CODE (addr))
{
case REG:
if (REGNO (addr) < FIRST_PSEUDO_REGISTER)
cost = 0;
else
cost = 1;
break;
case SYMBOL_REF:
cost = 8;
break;
case LABEL_REF:
case CONST:
cost = 0;
break;
case PLUS:
{
register rtx plus0 = XEXP (addr, 0);
register rtx plus1 = XEXP (addr, 1);
if (GET_CODE (plus0) != REG)
break;
switch (GET_CODE (plus1))
{
case CONST_INT:
if (INTVAL (plus1) >= 2 * m68hc11_max_offset
|| INTVAL (plus1) < m68hc11_min_offset)
cost = 3;
else if (INTVAL (plus1) >= m68hc11_max_offset)
cost = 2;
else
cost = 1;
if (REGNO (plus0) < FIRST_PSEUDO_REGISTER)
cost += 0;
else
cost += 1;
break;
case SYMBOL_REF:
cost = 8;
break;
case CONST:
case LABEL_REF:
cost = 0;
break;
default:
break;
}
break;
}
case PRE_DEC:
case PRE_INC:
if (SP_REG_P (XEXP (addr, 0)))
cost = 1;
break;
default:
break;
}
if (debug_m6811)
{
printf ("Address cost: %d for :", cost);
fflush (stdout);
debug_rtx (addr);
}
return cost;
}
static int
m68hc11_shift_cost (enum machine_mode mode, rtx x, int shift)
{
int total;
total = rtx_cost (x, SET);
if (mode == QImode)
total += m68hc11_cost->shiftQI_const[shift % 8];
else if (mode == HImode)
total += m68hc11_cost->shiftHI_const[shift % 16];
else if (shift == 8 || shift == 16 || shift == 32)
total += m68hc11_cost->shiftHI_const[8];
else if (shift != 0 && shift != 16 && shift != 32)
{
total += m68hc11_cost->shiftHI_const[1] * shift;
}
if (GET_MODE_SIZE (mode) > 2 && (shift % 16) != 0)
total *= GET_MODE_SIZE (mode) / 2;
if (optimize_size && (shift % 8) != 0)
total *= 2;
return total;
}
static int
m68hc11_rtx_costs_1 (rtx x, enum rtx_code code,
enum rtx_code outer_code ATTRIBUTE_UNUSED)
{
enum machine_mode mode = GET_MODE (x);
int extra_cost = 0;
int total;
switch (code)
{
case ROTATE:
case ROTATERT:
case ASHIFT:
case LSHIFTRT:
case ASHIFTRT:
if (GET_CODE (XEXP (x, 1)) == CONST_INT)
{
return m68hc11_shift_cost (mode, XEXP (x, 0), INTVAL (XEXP (x, 1)));
}
total = rtx_cost (XEXP (x, 0), code) + rtx_cost (XEXP (x, 1), code);
total += m68hc11_cost->shift_var;
return total;
case AND:
case XOR:
case IOR:
total = rtx_cost (XEXP (x, 0), code) + rtx_cost (XEXP (x, 1), code);
total += m68hc11_cost->logical;
total *= GET_MODE_SIZE (mode);
return total;
case MINUS:
case PLUS:
total = rtx_cost (XEXP (x, 0), code) + rtx_cost (XEXP (x, 1), code);
total += m68hc11_cost->add;
if (GET_MODE_SIZE (mode) > 2)
{
total *= GET_MODE_SIZE (mode) / 2;
}
return total;
case UDIV:
case DIV:
case MOD:
total = rtx_cost (XEXP (x, 0), code) + rtx_cost (XEXP (x, 1), code);
switch (mode)
{
case QImode:
total += m68hc11_cost->divQI;
break;
case HImode:
total += m68hc11_cost->divHI;
break;
case SImode:
default:
total += m68hc11_cost->divSI;
break;
}
return total;
case MULT:
if (mode == HImode && GET_CODE (XEXP (x, 0)) == ZERO_EXTEND
&& GET_CODE (XEXP (x, 1)) == ZERO_EXTEND)
return m68hc11_cost->multQI
+ rtx_cost (XEXP (XEXP (x, 0), 0), code)
+ rtx_cost (XEXP (XEXP (x, 1), 0), code);
if (TARGET_M6812 && mode == SImode
&& GET_CODE (XEXP (x, 0)) == ZERO_EXTEND
&& GET_CODE (XEXP (x, 1)) == ZERO_EXTEND)
return m68hc11_cost->multHI
+ rtx_cost (XEXP (XEXP (x, 0), 0), code)
+ rtx_cost (XEXP (XEXP (x, 1), 0), code);
total = rtx_cost (XEXP (x, 0), code) + rtx_cost (XEXP (x, 1), code);
switch (mode)
{
case QImode:
total += m68hc11_cost->multQI;
break;
case HImode:
total += m68hc11_cost->multHI;
break;
case SImode:
default:
total += m68hc11_cost->multSI;
break;
}
return total;
case NEG:
case SIGN_EXTEND:
extra_cost = COSTS_N_INSNS (2);
case NOT:
case COMPARE:
case ABS:
case ZERO_EXTEND:
total = extra_cost + rtx_cost (XEXP (x, 0), code);
if (mode == QImode)
{
return total + COSTS_N_INSNS (1);
}
if (mode == HImode)
{
return total + COSTS_N_INSNS (2);
}
if (mode == SImode)
{
return total + COSTS_N_INSNS (4);
}
return total + COSTS_N_INSNS (8);
case IF_THEN_ELSE:
if (GET_CODE (XEXP (x, 1)) == PC || GET_CODE (XEXP (x, 2)) == PC)
return COSTS_N_INSNS (1);
return COSTS_N_INSNS (1);
default:
return COSTS_N_INSNS (4);
}
}
static bool
m68hc11_rtx_costs (rtx x, int code, int outer_code, int *total)
{
switch (code)
{
case CONST_INT:
case CONST:
case LABEL_REF:
case SYMBOL_REF:
case CONST_DOUBLE:
if (outer_code == SET && x == const0_rtx)
*total = 1 - reload_completed;
else
*total = 0;
return true;
case ROTATE:
case ROTATERT:
case ASHIFT:
case LSHIFTRT:
case ASHIFTRT:
case MINUS:
case PLUS:
case AND:
case XOR:
case IOR:
case UDIV:
case DIV:
case MOD:
case MULT:
case NEG:
case SIGN_EXTEND:
case NOT:
case COMPARE:
case ZERO_EXTEND:
case IF_THEN_ELSE:
*total = m68hc11_rtx_costs_1 (x, code, outer_code);
return true;
default:
return false;
}
}
static void
m68hc11_file_start (void)
{
default_file_start ();
fprintf (asm_out_file, "\t.mode %s\n", TARGET_SHORT ? "mshort" : "mlong");
}
static void
m68hc11_asm_out_constructor (rtx symbol, int priority)
{
default_ctor_section_asm_out_constructor (symbol, priority);
fprintf (asm_out_file, "\t.globl\t__do_global_ctors\n");
}
static void
m68hc11_asm_out_destructor (rtx symbol, int priority)
{
default_dtor_section_asm_out_destructor (symbol, priority);
fprintf (asm_out_file, "\t.globl\t__do_global_dtors\n");
}
static rtx
m68hc11_struct_value_rtx (tree fntype ATTRIBUTE_UNUSED,
int incoming ATTRIBUTE_UNUSED)
{
return gen_rtx_REG (Pmode, HARD_D_REGNUM);
}
static bool
m68hc11_return_in_memory (tree type, tree fntype ATTRIBUTE_UNUSED)
{
if (TYPE_MODE (type) == BLKmode)
{
HOST_WIDE_INT size = int_size_in_bytes (type);
return (size == -1 || size > 4);
}
else
return GET_MODE_SIZE (TYPE_MODE (type)) > 4;
}
#include "gt-m68hc11.h"