#include "config.h"
#include "system.h"
#include "rtl.h"
#include "tree.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 "function.h"
#include "toplev.h"
#include "c-pragma.h"
#include "tm_p.h"
#include "ggc.h"
#include "target.h"
#include "target-def.h"
static const char *byte_reg PARAMS ((rtx, int));
static int h8300_interrupt_function_p PARAMS ((tree));
static int h8300_monitor_function_p PARAMS ((tree));
static int h8300_os_task_function_p PARAMS ((tree));
static void dosize PARAMS ((FILE *, int, unsigned int));
static int round_frame_size PARAMS ((int));
static unsigned int compute_saved_regs PARAMS ((void));
static void push PARAMS ((FILE *, int));
static void pop PARAMS ((FILE *, int));
static const char *cond_string PARAMS ((enum rtx_code));
static unsigned int h8300_asm_insn_count PARAMS ((const char *));
const struct attribute_spec h8300_attribute_table[];
static tree h8300_handle_fndecl_attribute PARAMS ((tree *, tree, tree, int, bool *));
static tree h8300_handle_eightbit_data_attribute PARAMS ((tree *, tree, tree, int, bool *));
static tree h8300_handle_tiny_data_attribute PARAMS ((tree *, tree, tree, int, bool *));
static void h8300_output_function_prologue PARAMS ((FILE *, HOST_WIDE_INT));
static void h8300_output_function_epilogue PARAMS ((FILE *, HOST_WIDE_INT));
static void h8300_insert_attributes PARAMS ((tree, tree *));
#ifndef OBJECT_FORMAT_ELF
static void h8300_asm_named_section PARAMS ((const char *, unsigned int));
#endif
static void h8300_encode_label PARAMS ((tree));
static void h8300_encode_section_info PARAMS ((tree, int));
static const char *h8300_strip_name_encoding PARAMS ((const char *));
int cpu_type;
static int interrupt_handler;
static int os_task;
static int monitor;
static int pragma_saveall;
static const char *const names_big[] =
{ "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7" };
static const char *const names_extended[] =
{ "er0", "er1", "er2", "er3", "er4", "er5", "er6", "er7" };
static const char *const names_upper_extended[] =
{ "e0", "e1", "e2", "e3", "e4", "e5", "e6", "e7" };
const char * const *h8_reg_names;
const char *h8_push_op, *h8_pop_op, *h8_mov_op;
#undef TARGET_ATTRIBUTE_TABLE
#define TARGET_ATTRIBUTE_TABLE h8300_attribute_table
#undef TARGET_ASM_ALIGNED_HI_OP
#define TARGET_ASM_ALIGNED_HI_OP "\t.word\t"
#undef TARGET_ASM_FUNCTION_PROLOGUE
#define TARGET_ASM_FUNCTION_PROLOGUE h8300_output_function_prologue
#undef TARGET_ASM_FUNCTION_EPILOGUE
#define TARGET_ASM_FUNCTION_EPILOGUE h8300_output_function_epilogue
#undef TARGET_ENCODE_SECTION_INFO
#define TARGET_ENCODE_SECTION_INFO h8300_encode_section_info
#undef TARGET_STRIP_NAME_ENCODING
#define TARGET_STRIP_NAME_ENCODING h8300_strip_name_encoding
#undef TARGET_INSERT_ATTRIBUTES
#define TARGET_INSERT_ATTRIBUTES h8300_insert_attributes
struct gcc_target targetm = TARGET_INITIALIZER;
enum shift_alg
{
SHIFT_INLINE,
SHIFT_ROT_AND,
SHIFT_SPECIAL,
SHIFT_LOOP
};
enum shift_type
{
SHIFT_ASHIFT, SHIFT_LSHIFTRT, SHIFT_ASHIFTRT
};
#define INL SHIFT_INLINE
#define ROT SHIFT_ROT_AND
#define LOP SHIFT_LOOP
#define SPC SHIFT_SPECIAL
static enum shift_alg shift_alg_qi[3][3][8] = {
{
{ INL, INL, INL, INL, INL, ROT, ROT, ROT },
{ INL, INL, INL, INL, INL, ROT, ROT, ROT },
{ INL, INL, INL, INL, INL, LOP, LOP, SPC }
},
{
{ INL, INL, INL, INL, INL, ROT, ROT, ROT },
{ INL, INL, INL, INL, INL, ROT, ROT, ROT },
{ INL, INL, INL, INL, INL, LOP, LOP, SPC }
},
{
{ INL, INL, INL, INL, INL, INL, ROT, ROT },
{ INL, INL, INL, INL, INL, INL, ROT, ROT },
{ INL, INL, INL, INL, INL, INL, INL, SPC }
}
};
static enum shift_alg shift_alg_hi[3][3][16] = {
{
{ INL, INL, INL, INL, INL, INL, INL, SPC,
SPC, SPC, SPC, SPC, SPC, SPC, SPC, SPC },
{ INL, INL, INL, INL, INL, LOP, LOP, SPC,
SPC, SPC, SPC, SPC, SPC, SPC, SPC, SPC },
{ INL, INL, INL, INL, INL, LOP, LOP, SPC,
SPC, SPC, SPC, SPC, SPC, SPC, SPC, SPC },
},
{
{ INL, INL, INL, INL, INL, INL, INL, SPC,
SPC, SPC, SPC, SPC, SPC, ROT, ROT, ROT },
{ INL, INL, INL, INL, INL, INL, INL, SPC,
SPC, SPC, SPC, SPC, SPC, ROT, ROT, ROT },
{ INL, INL, INL, INL, INL, INL, INL, SPC,
SPC, SPC, SPC, SPC, SPC, SPC, SPC, SPC },
},
{
{ INL, INL, INL, INL, INL, INL, INL, INL,
SPC, SPC, SPC, SPC, SPC, ROT, ROT, ROT },
{ INL, INL, INL, INL, INL, INL, INL, INL,
SPC, SPC, SPC, SPC, SPC, ROT, ROT, ROT },
{ INL, INL, INL, INL, INL, INL, INL, INL,
SPC, SPC, SPC, SPC, SPC, SPC, SPC, SPC },
}
};
static enum shift_alg shift_alg_si[3][3][32] = {
{
{ INL, INL, INL, LOP, LOP, LOP, LOP, LOP,
SPC, LOP, LOP, LOP, LOP, LOP, LOP, LOP,
SPC, SPC, SPC, SPC, SPC, LOP, LOP, LOP,
SPC, SPC, SPC, SPC, LOP, LOP, LOP, SPC },
{ INL, INL, INL, LOP, LOP, LOP, LOP, LOP,
SPC, SPC, LOP, LOP, LOP, LOP, LOP, SPC,
SPC, SPC, SPC, LOP, LOP, LOP, LOP, LOP,
SPC, SPC, SPC, SPC, SPC, LOP, LOP, SPC },
{ INL, INL, INL, LOP, LOP, LOP, LOP, LOP,
SPC, LOP, LOP, LOP, LOP, LOP, LOP, SPC,
SPC, SPC, LOP, LOP, LOP, LOP, LOP, LOP,
SPC, SPC, SPC, LOP, LOP, LOP, LOP, SPC },
},
{
{ INL, INL, INL, INL, INL, LOP, LOP, LOP,
SPC, LOP, LOP, LOP, LOP, LOP, LOP, SPC,
SPC, SPC, SPC, SPC, LOP, LOP, LOP, LOP,
SPC, LOP, LOP, LOP, SPC, SPC, SPC, SPC },
{ INL, INL, INL, INL, INL, LOP, LOP, LOP,
SPC, LOP, LOP, LOP, LOP, LOP, LOP, SPC,
SPC, SPC, SPC, SPC, LOP, LOP, LOP, LOP,
SPC, LOP, LOP, LOP, SPC, SPC, SPC, SPC },
{ INL, INL, INL, INL, INL, LOP, LOP, LOP,
SPC, LOP, LOP, LOP, LOP, LOP, LOP, LOP,
SPC, SPC, SPC, SPC, LOP, LOP, LOP, LOP,
SPC, LOP, LOP, LOP, LOP, LOP, LOP, SPC },
},
{
{ INL, INL, INL, INL, INL, INL, INL, INL,
INL, INL, INL, LOP, LOP, LOP, LOP, SPC,
SPC, SPC, SPC, SPC, SPC, SPC, LOP, LOP,
SPC, SPC, LOP, LOP, SPC, SPC, SPC, SPC },
{ INL, INL, INL, INL, INL, INL, INL, INL,
INL, INL, INL, LOP, LOP, LOP, LOP, SPC,
SPC, SPC, SPC, SPC, SPC, SPC, LOP, LOP,
SPC, SPC, LOP, LOP, SPC, SPC, SPC, SPC },
{ INL, INL, INL, INL, INL, INL, INL, INL,
INL, INL, INL, LOP, LOP, LOP, LOP, LOP,
SPC, SPC, SPC, SPC, SPC, SPC, LOP, LOP,
SPC, SPC, LOP, LOP, LOP, LOP, LOP, SPC },
}
};
#undef INL
#undef ROT
#undef LOP
#undef SPC
enum h8_cpu
{
H8_300,
H8_300H,
H8_S
};
void
h8300_init_once ()
{
static const char *const h8_push_ops[2] = { "push" , "push.l" };
static const char *const h8_pop_ops[2] = { "pop" , "pop.l" };
static const char *const h8_mov_ops[2] = { "mov.w", "mov.l" };
if (TARGET_H8300)
{
cpu_type = (int) CPU_H8300;
h8_reg_names = names_big;
}
else
{
cpu_type = (int) CPU_H8300H;
h8_reg_names = names_extended;
}
h8_push_op = h8_push_ops[cpu_type];
h8_pop_op = h8_pop_ops[cpu_type];
h8_mov_op = h8_mov_ops[cpu_type];
if (!TARGET_H8300S && TARGET_MAC)
{
error ("-ms2600 is used without -ms");
target_flags |= MASK_H8300S;
}
if (TARGET_H8300 && TARGET_NORMAL_MODE)
{
error ("-mn is used without -mh or -ms");
target_flags ^= MASK_NORMAL_MODE;
}
if (optimize_size)
{
shift_alg_hi[H8_300][SHIFT_ASHIFT][5] = SHIFT_LOOP;
shift_alg_hi[H8_300][SHIFT_ASHIFT][6] = SHIFT_LOOP;
shift_alg_hi[H8_300][SHIFT_ASHIFT][13] = SHIFT_LOOP;
shift_alg_hi[H8_300][SHIFT_ASHIFT][14] = SHIFT_LOOP;
shift_alg_hi[H8_300][SHIFT_LSHIFTRT][13] = SHIFT_LOOP;
shift_alg_hi[H8_300][SHIFT_LSHIFTRT][14] = SHIFT_LOOP;
shift_alg_hi[H8_300][SHIFT_ASHIFTRT][13] = SHIFT_LOOP;
shift_alg_hi[H8_300][SHIFT_ASHIFTRT][14] = SHIFT_LOOP;
shift_alg_hi[H8_300H][SHIFT_ASHIFT][5] = SHIFT_LOOP;
shift_alg_hi[H8_300H][SHIFT_ASHIFT][6] = SHIFT_LOOP;
shift_alg_hi[H8_300H][SHIFT_LSHIFTRT][5] = SHIFT_LOOP;
shift_alg_hi[H8_300H][SHIFT_LSHIFTRT][6] = SHIFT_LOOP;
shift_alg_hi[H8_300H][SHIFT_ASHIFTRT][5] = SHIFT_LOOP;
shift_alg_hi[H8_300H][SHIFT_ASHIFTRT][6] = SHIFT_LOOP;
shift_alg_hi[H8_300H][SHIFT_ASHIFTRT][13] = SHIFT_LOOP;
shift_alg_hi[H8_300H][SHIFT_ASHIFTRT][14] = SHIFT_LOOP;
shift_alg_hi[H8_S][SHIFT_ASHIFTRT][14] = SHIFT_LOOP;
}
}
static const char *
byte_reg (x, b)
rtx x;
int b;
{
static const char *const names_small[] = {
"r0l", "r0h", "r1l", "r1h", "r2l", "r2h", "r3l", "r3h",
"r4l", "r4h", "r5l", "r5h", "r6l", "r6h", "r7l", "r7h"
};
return names_small[REGNO (x) * 2 + b];
}
#define WORD_REG_USED(regno) \
(regno < 7 \
\
&& ! TREE_THIS_VOLATILE (current_function_decl) \
&& (pragma_saveall \
\
|| (regs_ever_live[regno] && !call_used_regs[regno]) \
\
|| (regno == FRAME_POINTER_REGNUM && regs_ever_live[regno]) \
\
|| (interrupt_handler && regs_ever_live[regno]) \
\
|| (interrupt_handler \
&& call_used_regs[regno] \
&& !current_function_is_leaf)))
static void
dosize (file, sign, size)
FILE *file;
int sign;
unsigned int size;
{
if ((TARGET_H8300 && size <= 4)
|| ((TARGET_H8300H || TARGET_H8300S) && size <= 8)
|| (TARGET_H8300 && interrupt_handler)
|| (TARGET_H8300 && current_function_needs_context
&& sign < 0))
{
const char *op = (sign > 0) ? "add" : "sub";
unsigned HOST_WIDE_INT amount;
for (amount = (TARGET_H8300H || TARGET_H8300S) ? 4 : 2;
amount > 0;
amount /= 2)
{
char insn[100];
sprintf (insn, "\t%ss\t#%d,%s\n", op, amount,
TARGET_H8300 ? "r7" : "er7");
for (; size >= amount; size -= amount)
fputs (insn, file);
}
}
else
{
if (TARGET_H8300)
{
fprintf (file, "\tmov.w\t#%d,r3\n\tadd.w\tr3,r7\n", sign * size);
}
else
{
fprintf (file, "\tadd.l\t#%d,er7\n", sign * size);
}
}
}
static int
round_frame_size (size)
int size;
{
return ((size + STACK_BOUNDARY / BITS_PER_UNIT - 1)
& -STACK_BOUNDARY / BITS_PER_UNIT);
}
static unsigned int
compute_saved_regs ()
{
unsigned int saved_regs = 0;
int regno;
for (regno = 0; regno <= FRAME_POINTER_REGNUM; regno++)
{
if (WORD_REG_USED (regno))
saved_regs |= 1 << regno;
}
if (frame_pointer_needed)
saved_regs &= ~(1 << FRAME_POINTER_REGNUM);
return saved_regs;
}
static void
push (file, rn)
FILE *file;
int rn;
{
if (TARGET_H8300)
fprintf (file, "\t%s\t%s,@-r7\n", h8_mov_op, h8_reg_names[rn]);
else
fprintf (file, "\t%s\t%s,@-er7\n", h8_mov_op, h8_reg_names[rn]);
}
static void
pop (file, rn)
FILE *file;
int rn;
{
if (TARGET_H8300)
fprintf (file, "\t%s\t@r7+,%s\n", h8_mov_op, h8_reg_names[rn]);
else
fprintf (file, "\t%s\t@er7+,%s\n", h8_mov_op, h8_reg_names[rn]);
}
static void
h8300_output_function_prologue (file, size)
FILE *file;
HOST_WIDE_INT size;
{
int fsize = round_frame_size (size);
int regno;
int saved_regs;
int n_regs;
if (h8300_interrupt_function_p (current_function_decl))
interrupt_handler = 1;
if (h8300_os_task_function_p (current_function_decl))
{
fprintf (file, ";OS_Task prologue\n");
os_task = 1;
return;
}
if (h8300_monitor_function_p (current_function_decl))
{
fprintf (file, ";monitor prologue\n");
interrupt_handler = 1;
monitor = 1;
if (TARGET_H8300)
{
fprintf (file, "\tsubs\t#2,sp\n");
push (file, 0);
fprintf (file, "\tstc\tccr,r0l\n");
fprintf (file, "\tmov.b\tr0l,@(2,sp)\n");
pop (file, 0);
fprintf (file, "\torc\t#128,ccr\n");
}
else if (TARGET_H8300H)
{
push (file, 0);
fprintf (file, "\tstc\tccr,r0l\n");
fprintf (file, "\tmov.b\tr0l,@(4,sp)\n");
pop (file, 0);
fprintf (file, "\torc\t#128,ccr\n");
}
else if (TARGET_H8300S)
{
fprintf (file, "\tstc\texr,@-sp\n");
push (file, 0);
fprintf (file, "\tstc\tccr,r0l\n");
fprintf (file, "\tmov.b\tr0l,@(6,sp)\n");
pop (file, 0);
fprintf (file, "\torc\t#128,ccr\n");
}
else
abort ();
}
if (frame_pointer_needed)
{
push (file, FRAME_POINTER_REGNUM);
fprintf (file, "\t%s\t%s,%s\n", h8_mov_op,
h8_reg_names[STACK_POINTER_REGNUM],
h8_reg_names[FRAME_POINTER_REGNUM]);
}
dosize (file, -1, fsize);
saved_regs = compute_saved_regs ();
for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno += n_regs)
{
n_regs = 1;
if (saved_regs & (1 << regno))
{
if (TARGET_H8300S)
{
if ((regno == 0 || regno == 4)
&& ((saved_regs >> regno) & 0x0f) == 0x0f)
n_regs = 4;
else if ((regno == 0 || regno == 4)
&& ((saved_regs >> regno) & 0x07) == 0x07)
n_regs = 3;
else if ((regno == 0 || regno == 2 || regno == 4 || regno == 6)
&& ((saved_regs >> regno) & 0x03) == 0x03)
n_regs = 2;
}
switch (n_regs)
{
case 1:
push (file, regno);
break;
case 2:
fprintf (file, "\tstm.l\t%s-%s,@-er7\n",
h8_reg_names[regno],
h8_reg_names[regno + 1]);
break;
case 3:
fprintf (file, "\tstm.l\t%s-%s,@-er7\n",
h8_reg_names[regno],
h8_reg_names[regno + 2]);
break;
case 4:
fprintf (file, "\tstm.l\t%s-%s,@-er7\n",
h8_reg_names[regno],
h8_reg_names[regno + 3]);
break;
default:
abort ();
}
}
}
}
static void
h8300_output_function_epilogue (file, size)
FILE *file;
HOST_WIDE_INT size;
{
int fsize = round_frame_size (size);
int regno;
rtx insn = get_last_insn ();
int saved_regs;
int n_regs;
if (os_task)
{
fprintf (file, ";OS_task epilogue\n");
fprintf (file, "\trts\n");
goto out;
}
if (monitor)
fprintf (file, ";monitor epilogue\n");
if (GET_CODE (insn) == NOTE)
insn = prev_nonnote_insn (insn);
if (insn && GET_CODE (insn) == BARRIER)
goto out;
saved_regs = compute_saved_regs ();
for (regno = FIRST_PSEUDO_REGISTER - 1; regno >= 0; regno -= n_regs)
{
n_regs = 1;
if (saved_regs & (1 << regno))
{
if (TARGET_H8300S)
{
if ((regno == 7 || regno == 3)
&& ((saved_regs >> (regno - 3)) & 0x0f) == 0x0f)
n_regs = 4;
else if ((regno == 6 || regno == 2)
&& ((saved_regs >> (regno - 2)) & 0x07) == 0x07)
n_regs = 3;
else if ((regno == 7 || regno == 5 || regno == 3 || regno == 1)
&& ((saved_regs >> (regno - 1)) & 0x03) == 0x03)
n_regs = 2;
}
switch (n_regs)
{
case 1:
pop (file, regno);
break;
case 2:
fprintf (file, "\tldm.l\t@er7+,%s-%s\n",
h8_reg_names[regno - 1],
h8_reg_names[regno]);
break;
case 3:
fprintf (file, "\tldm.l\t@er7+,%s-%s\n",
h8_reg_names[regno - 2],
h8_reg_names[regno]);
break;
case 4:
fprintf (file, "\tldm.l\t@er7+,%s-%s\n",
h8_reg_names[regno - 3],
h8_reg_names[regno]);
break;
default:
abort ();
}
}
}
dosize (file, 1, fsize);
if (frame_pointer_needed)
pop (file, FRAME_POINTER_REGNUM);
if (interrupt_handler)
fprintf (file, "\trte\n");
else
fprintf (file, "\trts\n");
out:
interrupt_handler = 0;
os_task = 0;
monitor = 0;
pragma_saveall = 0;
}
void
asm_file_start (file)
FILE *file;
{
fprintf (file, ";\tGCC For the Hitachi H8/300\n");
fprintf (file, ";\tBy Hitachi America Ltd and Cygnus Support\n");
if (optimize_size)
fprintf (file, "; -Os\n");
else if (optimize)
fprintf (file, "; -O%d\n", optimize);
if (TARGET_H8300H)
fprintf (file, "\n\t.h8300h\n");
else if (TARGET_H8300S)
fprintf (file, "\n\t.h8300s\n");
else
fprintf (file, "\n\n");
output_file_directive (file, main_input_filename);
}
void
asm_file_end (file)
FILE *file;
{
fprintf (file, "\t.end\n");
}
int
general_operand_src (op, mode)
rtx op;
enum machine_mode mode;
{
if (GET_MODE (op) == mode
&& GET_CODE (op) == MEM
&& GET_CODE (XEXP (op, 0)) == POST_INC)
return 1;
return general_operand (op, mode);
}
int
general_operand_dst (op, mode)
rtx op;
enum machine_mode mode;
{
if (GET_MODE (op) == mode
&& GET_CODE (op) == MEM
&& GET_CODE (XEXP (op, 0)) == PRE_DEC)
return 1;
return general_operand (op, mode);
}
int
single_one_operand (operand, mode)
rtx operand;
enum machine_mode mode ATTRIBUTE_UNUSED;
{
if (GET_CODE (operand) == CONST_INT)
{
unsigned HOST_WIDE_INT mask =
(GET_MODE_BITSIZE (mode) < HOST_BITS_PER_WIDE_INT)
? ((unsigned HOST_WIDE_INT) 1 << GET_MODE_BITSIZE (mode)) - 1
: ~(unsigned HOST_WIDE_INT) 0;
unsigned HOST_WIDE_INT value = INTVAL (operand);
if (exact_log2 (value & mask) >= 0)
return 1;
}
return 0;
}
int
single_zero_operand (operand, mode)
rtx operand;
enum machine_mode mode ATTRIBUTE_UNUSED;
{
if (GET_CODE (operand) == CONST_INT)
{
unsigned HOST_WIDE_INT mask =
(GET_MODE_BITSIZE (mode) < HOST_BITS_PER_WIDE_INT)
? ((unsigned HOST_WIDE_INT) 1 << GET_MODE_BITSIZE (mode)) - 1
: ~(unsigned HOST_WIDE_INT) 0;
unsigned HOST_WIDE_INT value = INTVAL (operand);
if (exact_log2 (~value & mask) >= 0)
return 1;
}
return 0;
}
int
call_insn_operand (op, mode)
rtx op;
enum machine_mode mode ATTRIBUTE_UNUSED;
{
if (GET_CODE (op) == MEM)
{
rtx inside = XEXP (op, 0);
if (register_operand (inside, Pmode))
return 1;
if (CONSTANT_ADDRESS_P (inside))
return 1;
}
return 0;
}
int
two_insn_adds_subs_operand (op, mode)
rtx op;
enum machine_mode mode;
{
if (GET_CODE (op) == CONST_INT)
{
HOST_WIDE_INT value = INTVAL (op);
if (value < 0)
value = -value;
if (TARGET_H8300H || TARGET_H8300S)
{
if (mode == SImode
&& (value == 2 + 1
|| value == 4 + 1
|| value == 4 + 2
|| value == 4 + 4))
return 1;
}
else
{
if (mode == HImode
&& (value == 2 + 1
|| value == 2 + 2))
return 1;
}
}
return 0;
}
void
split_adds_subs (mode, operands)
enum machine_mode mode;
rtx *operands;
{
HOST_WIDE_INT val = INTVAL (operands[1]);
rtx reg = operands[0];
HOST_WIDE_INT sign = 1;
HOST_WIDE_INT amount;
if (val < 0)
{
val = -val;
sign = -1;
}
for (amount = (TARGET_H8300H || TARGET_H8300S) ? 4 : 2;
amount > 0;
amount /= 2)
{
for (; val >= amount; val -= amount)
{
rtx tmp = gen_rtx_PLUS (mode, reg, GEN_INT (sign * amount));
emit_insn (gen_rtx_SET (VOIDmode, reg, tmp));
}
}
return;
}
int
small_call_insn_operand (op, mode)
rtx op;
enum machine_mode mode ATTRIBUTE_UNUSED;
{
if (GET_CODE (op) == MEM)
{
rtx inside = XEXP (op, 0);
if (register_operand (inside, Pmode))
return 1;
if (GET_CODE (inside) == SYMBOL_REF
&& SYMBOL_REF_FLAG (inside))
return 1;
}
return 0;
}
int
jump_address_operand (op, mode)
rtx op;
enum machine_mode mode;
{
if (GET_CODE (op) == REG)
return mode == Pmode;
if (GET_CODE (op) == MEM)
{
rtx inside = XEXP (op, 0);
if (register_operand (inside, Pmode))
return 1;
if (CONSTANT_ADDRESS_P (inside))
return 1;
}
return 0;
}
extern int rtx_equal_function_value_matters;
int
bit_operand (op, mode)
rtx op;
enum machine_mode mode;
{
if (!general_operand (op, mode))
return 0;
if (GET_CODE (op) == REG)
return 1;
if (GET_CODE (op) == SUBREG)
return 1;
if (!rtx_equal_function_value_matters)
return GET_CODE (op) == MEM;
else
return (GET_CODE (op) == MEM
&& EXTRA_CONSTRAINT (op, 'U'));
}
int
bit_memory_operand (op, mode)
rtx op;
enum machine_mode mode ATTRIBUTE_UNUSED;
{
return (GET_CODE (op) == MEM
&& EXTRA_CONSTRAINT (op, 'U'));
}
void
h8300_pr_interrupt (pfile)
cpp_reader *pfile ATTRIBUTE_UNUSED;
{
interrupt_handler = 1;
}
void
h8300_pr_saveall (pfile)
cpp_reader *pfile ATTRIBUTE_UNUSED;
{
pragma_saveall = 1;
}
rtx
function_arg (cum, mode, type, named)
CUMULATIVE_ARGS *cum;
enum machine_mode mode;
tree type;
int named;
{
static const char *const hand_list[] = {
"__main",
"__cmpsi2",
"__divhi3",
"__modhi3",
"__udivhi3",
"__umodhi3",
"__divsi3",
"__modsi3",
"__udivsi3",
"__umodsi3",
"__mulhi3",
"__mulsi3",
"__reg_memcpy",
"__reg_memset",
"__ucmpsi2",
0,
};
rtx result = NULL_RTX;
const char *fname;
int regpass = 0;
if (!named)
return NULL_RTX;
if (TARGET_QUICKCALL)
regpass = 3;
if (cum->libcall)
{
const char * const *p;
fname = XSTR (cum->libcall, 0);
for (p = hand_list; *p && strcmp (*p, fname) != 0; p++)
;
if (*p)
regpass = 4;
}
if (regpass)
{
int size;
if (mode == BLKmode)
size = int_size_in_bytes (type);
else
size = GET_MODE_SIZE (mode);
if (size + cum->nbytes <= regpass * UNITS_PER_WORD
&& cum->nbytes / UNITS_PER_WORD <= 3)
result = gen_rtx_REG (mode, cum->nbytes / UNITS_PER_WORD);
}
return result;
}
int
const_costs (r, c, outer_code)
rtx r;
enum rtx_code c;
enum rtx_code outer_code;
{
switch (c)
{
case CONST_INT:
switch (INTVAL (r))
{
case 0:
return 0;
case 1:
case 2:
case -1:
case -2:
return 0 + (outer_code == SET);
case 4:
case -4:
if (TARGET_H8300H || TARGET_H8300S)
return 0 + (outer_code == SET);
else
return 1;
default:
return 1;
}
case CONST:
case LABEL_REF:
case SYMBOL_REF:
return 3;
case CONST_DOUBLE:
return 20;
default:
return 4;
}
}
int
h8300_and_costs (x)
rtx x;
{
rtx operands[4];
if (GET_MODE (x) == QImode)
return 1;
if (GET_MODE (x) != HImode
&& GET_MODE (x) != SImode)
return 100;
operands[0] = NULL;
operands[1] = NULL;
operands[2] = XEXP (x, 1);
operands[3] = x;
return compute_logical_op_length (GET_MODE (x), operands);
}
int
h8300_shift_costs (x)
rtx x;
{
rtx operands[4];
if (GET_MODE (x) != QImode
&& GET_MODE (x) != HImode
&& GET_MODE (x) != SImode)
return 100;
operands[0] = NULL;
operands[1] = NULL;
operands[2] = XEXP (x, 1);
operands[3] = x;
return compute_a_shift_length (NULL, operands);
}
static const char *
cond_string (code)
enum rtx_code code;
{
switch (code)
{
case NE:
return "ne";
case EQ:
return "eq";
case GE:
return "ge";
case GT:
return "gt";
case LE:
return "le";
case LT:
return "lt";
case GEU:
return "hs";
case GTU:
return "hi";
case LEU:
return "ls";
case LTU:
return "lo";
default:
abort ();
}
}
void
print_operand (file, x, code)
FILE *file;
rtx x;
int code;
{
static int bitint;
switch (code)
{
case 'E':
switch (GET_CODE (x))
{
case REG:
fprintf (file, "%sl", names_big[REGNO (x)]);
break;
case CONST_INT:
fprintf (file, "#%d", (-INTVAL (x)) & 0xff);
break;
default:
abort ();
}
break;
case 'F':
switch (GET_CODE (x))
{
case REG:
fprintf (file, "%sh", names_big[REGNO (x)]);
break;
case CONST_INT:
fprintf (file, "#%d", ((-INTVAL (x)) & 0xff00) >> 8);
break;
default:
abort ();
}
break;
case 'G':
if (GET_CODE (x) != CONST_INT)
abort ();
fprintf (file, "#%d", 0xff & (-INTVAL (x)));
break;
case 'S':
if (GET_CODE (x) == REG)
fprintf (file, "%s", names_extended[REGNO (x)]);
else
goto def;
break;
case 'T':
if (GET_CODE (x) == REG)
fprintf (file, "%s", names_big[REGNO (x)]);
else
goto def;
break;
case 'V':
bitint = exact_log2 (INTVAL (x) & 0xff);
if (bitint == -1)
abort ();
fprintf (file, "#%d", bitint);
break;
case 'W':
bitint = exact_log2 ((~INTVAL (x)) & 0xff);
if (bitint == -1)
abort ();
fprintf (file, "#%d", bitint);
break;
case 'R':
case 'X':
if (GET_CODE (x) == REG)
fprintf (file, "%s", byte_reg (x, 0));
else
goto def;
break;
case 'Y':
if (bitint == -1)
abort ();
if (GET_CODE (x) == REG)
fprintf (file, "%s%c", names_big[REGNO (x)], bitint > 7 ? 'h' : 'l');
else
print_operand (file, x, 'R');
bitint = -1;
break;
case 'Z':
bitint = INTVAL (x);
fprintf (file, "#%d", bitint & 7);
break;
case 'b':
switch (GET_CODE (x))
{
case IOR:
fprintf (file, "bor");
break;
case XOR:
fprintf (file, "bxor");
break;
case AND:
fprintf (file, "band");
break;
default:
break;
}
break;
case 'e':
switch (GET_CODE (x))
{
case REG:
if (TARGET_H8300)
fprintf (file, "%s", names_big[REGNO (x)]);
else
fprintf (file, "%s", names_upper_extended[REGNO (x)]);
break;
case MEM:
print_operand (file, x, 0);
break;
case CONST_INT:
fprintf (file, "#%d", ((INTVAL (x) >> 16) & 0xffff));
break;
case CONST_DOUBLE:
{
long val;
REAL_VALUE_TYPE rv;
REAL_VALUE_FROM_CONST_DOUBLE (rv, x);
REAL_VALUE_TO_TARGET_SINGLE (rv, val);
fprintf (file, "#%ld", ((val >> 16) & 0xffff));
break;
}
default:
abort ();
break;
}
break;
case 'f':
switch (GET_CODE (x))
{
case REG:
if (TARGET_H8300)
fprintf (file, "%s", names_big[REGNO (x) + 1]);
else
fprintf (file, "%s", names_big[REGNO (x)]);
break;
case MEM:
x = adjust_address (x, HImode, 2);
print_operand (file, x, 0);
break;
case CONST_INT:
fprintf (file, "#%d", INTVAL (x) & 0xffff);
break;
case CONST_DOUBLE:
{
long val;
REAL_VALUE_TYPE rv;
REAL_VALUE_FROM_CONST_DOUBLE (rv, x);
REAL_VALUE_TO_TARGET_SINGLE (rv, val);
fprintf (file, "#%ld", (val & 0xffff));
break;
}
default:
abort ();
}
break;
case 'j':
fputs (cond_string (GET_CODE (x)), file);
break;
case 'k':
fputs (cond_string (reverse_condition (GET_CODE (x))), file);
break;
case 's':
if (GET_CODE (x) == CONST_INT)
fprintf (file, "#%d", (INTVAL (x)) & 0xff);
else
fprintf (file, "%s", byte_reg (x, 0));
break;
case 't':
if (GET_CODE (x) == CONST_INT)
fprintf (file, "#%d", (INTVAL (x) >> 8) & 0xff);
else
fprintf (file, "%s", byte_reg (x, 1));
break;
case 'u':
if (GET_CODE (x) != CONST_INT)
abort ();
fprintf (file, "%d", INTVAL (x));
break;
case 'w':
if (GET_CODE (x) == CONST_INT)
fprintf (file, "#%d", INTVAL (x) & 0xff);
else
fprintf (file, "%s",
byte_reg (x, TARGET_H8300 ? 2 : 0));
break;
case 'x':
if (GET_CODE (x) == CONST_INT)
fprintf (file, "#%d", (INTVAL (x) >> 8) & 0xff);
else
fprintf (file, "%s",
byte_reg (x, TARGET_H8300 ? 3 : 1));
break;
case 'y':
if (GET_CODE (x) == CONST_INT)
fprintf (file, "#%d", (INTVAL (x) >> 16) & 0xff);
else
fprintf (file, "%s", byte_reg (x, 0));
break;
case 'z':
if (GET_CODE (x) == CONST_INT)
fprintf (file, "#%d", (INTVAL (x) >> 24) & 0xff);
else
fprintf (file, "%s", byte_reg (x, 1));
break;
default:
def:
switch (GET_CODE (x))
{
case REG:
switch (GET_MODE (x))
{
case QImode:
#if 0
fprintf (file, "%s", byte_reg (x, 0));
#else
fprintf (file, "%s", names_big[REGNO (x)]);
#endif
break;
case HImode:
fprintf (file, "%s", names_big[REGNO (x)]);
break;
case SImode:
case SFmode:
fprintf (file, "%s", names_extended[REGNO (x)]);
break;
default:
abort ();
}
break;
case MEM:
{
rtx addr = XEXP (x, 0);
fprintf (file, "@");
output_address (addr);
switch (code)
{
case 'R':
if (h8300_eightbit_constant_address_p (addr))
{
fprintf (file, ":8");
break;
}
case 'T':
case 'S':
if (h8300_tiny_constant_address_p (addr))
fprintf (file, ":16");
break;
default:
break;
}
}
break;
case CONST_INT:
case SYMBOL_REF:
case CONST:
case LABEL_REF:
fprintf (file, "#");
print_operand_address (file, x);
break;
case CONST_DOUBLE:
{
long val;
REAL_VALUE_TYPE rv;
REAL_VALUE_FROM_CONST_DOUBLE (rv, x);
REAL_VALUE_TO_TARGET_SINGLE (rv, val);
fprintf (file, "#%ld", val);
break;
}
default:
break;
}
}
}
void
print_operand_address (file, addr)
FILE *file;
rtx addr;
{
switch (GET_CODE (addr))
{
case REG:
fprintf (file, "%s", h8_reg_names[REGNO (addr)]);
break;
case PRE_DEC:
fprintf (file, "-%s", h8_reg_names[REGNO (XEXP (addr, 0))]);
break;
case POST_INC:
fprintf (file, "%s+", h8_reg_names[REGNO (XEXP (addr, 0))]);
break;
case PLUS:
fprintf (file, "(");
if (GET_CODE (XEXP (addr, 0)) == REG)
{
print_operand_address (file, XEXP (addr, 1));
fprintf (file, ",");
print_operand_address (file, XEXP (addr, 0));
}
else
{
print_operand_address (file, XEXP (addr, 0));
fprintf (file, "+");
print_operand_address (file, XEXP (addr, 1));
}
fprintf (file, ")");
break;
case CONST_INT:
{
int n = INTVAL (addr);
if (TARGET_H8300)
n = (int) (short) n;
if (n < 0)
fprintf (file, "-%d", -n);
else
fprintf (file, "%d", n);
break;
}
default:
output_addr_const (file, addr);
break;
}
}
void
final_prescan_insn (insn, operand, num_operands)
rtx insn, *operand ATTRIBUTE_UNUSED;
int num_operands ATTRIBUTE_UNUSED;
{
static int last_insn_address = 0;
int uid = INSN_UID (insn);
if (TARGET_RTL_DUMP)
{
fprintf (asm_out_file, "\n****************");
print_rtl (asm_out_file, PATTERN (insn));
fprintf (asm_out_file, "\n");
}
if (TARGET_ADDRESSES)
{
fprintf (asm_out_file, "; 0x%x %d\n", INSN_ADDRESSES (uid),
INSN_ADDRESSES (uid) - last_insn_address);
last_insn_address = INSN_ADDRESSES (uid);
}
}
int
do_movsi (operands)
rtx operands[];
{
rtx src = operands[1];
rtx dst = operands[0];
if (!reload_in_progress && !reload_completed)
{
if (!register_operand (dst, GET_MODE (dst)))
{
rtx tmp = gen_reg_rtx (GET_MODE (dst));
emit_move_insn (tmp, src);
operands[1] = tmp;
}
}
return 0;
}
int
h8300_initial_elimination_offset (from, to)
int from, to;
{
int offset = 0;
if (from == ARG_POINTER_REGNUM && to == FRAME_POINTER_REGNUM)
offset = UNITS_PER_WORD + frame_pointer_needed * UNITS_PER_WORD;
else if (from == RETURN_ADDRESS_POINTER_REGNUM && to == FRAME_POINTER_REGNUM)
offset = frame_pointer_needed * UNITS_PER_WORD;
else
{
int regno;
for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
if (WORD_REG_USED (regno))
offset += UNITS_PER_WORD;
offset += round_frame_size (get_frame_size ());
if (from == ARG_POINTER_REGNUM && to == STACK_POINTER_REGNUM)
offset += UNITS_PER_WORD;
}
if ((TARGET_H8300H || TARGET_H8300S) && TARGET_NORMAL_MODE)
offset -= 2;
return offset;
}
rtx
h8300_return_addr_rtx (count, frame)
int count;
rtx frame;
{
rtx ret;
if (count == 0)
ret = gen_rtx_MEM (Pmode,
gen_rtx_REG (Pmode, RETURN_ADDRESS_POINTER_REGNUM));
else if (flag_omit_frame_pointer)
return (rtx) 0;
else
ret = gen_rtx_MEM (Pmode,
memory_address (Pmode,
plus_constant (frame, UNITS_PER_WORD)));
set_mem_alias_set (ret, get_frame_alias_set ());
return ret;
}
void
notice_update_cc (body, insn)
rtx body;
rtx insn;
{
rtx set;
switch (get_attr_cc (insn))
{
case CC_NONE:
break;
case CC_NONE_0HIT:
if (cc_status.value1 != 0
&& reg_overlap_mentioned_p (recog_data.operand[0], cc_status.value1))
cc_status.value1 = 0;
if (cc_status.value2 != 0
&& reg_overlap_mentioned_p (recog_data.operand[0], cc_status.value2))
cc_status.value2 = 0;
break;
case CC_SET_ZN:
CC_STATUS_INIT;
cc_status.flags |= CC_OVERFLOW_UNUSABLE | CC_NO_CARRY;
set = single_set (insn);
cc_status.value1 = SET_SRC (set);
if (SET_DEST (set) != cc0_rtx)
cc_status.value2 = SET_DEST (set);
break;
case CC_SET_ZNV:
CC_STATUS_INIT;
cc_status.flags |= CC_NO_CARRY;
set = single_set (insn);
cc_status.value1 = SET_SRC (set);
if (SET_DEST (set) != cc0_rtx)
cc_status.value2 = SET_DEST (set);
break;
case CC_COMPARE:
CC_STATUS_INIT;
cc_status.value1 = SET_SRC (body);
break;
case CC_CLOBBER:
CC_STATUS_INIT;
break;
}
}
int
stack_pointer_operand (x, mode)
rtx x;
enum machine_mode mode ATTRIBUTE_UNUSED;
{
return x == stack_pointer_rtx;
}
int
const_int_gt_2_operand (x, mode)
rtx x;
enum machine_mode mode ATTRIBUTE_UNUSED;
{
return (GET_CODE (x) == CONST_INT
&& abs (INTVAL (x)) > 2);
}
int
const_int_ge_8_operand (x, mode)
rtx x;
enum machine_mode mode ATTRIBUTE_UNUSED;
{
return (GET_CODE (x) == CONST_INT
&& abs (INTVAL (x)) >= 8);
}
int
bit_operator (x, mode)
rtx x;
enum machine_mode mode ATTRIBUTE_UNUSED;
{
enum rtx_code code = GET_CODE (x);
return (code == XOR
|| code == AND
|| code == IOR);
}
const char *
output_logical_op (mode, operands)
enum machine_mode mode;
rtx *operands;
{
enum rtx_code code = GET_CODE (operands[3]);
unsigned HOST_WIDE_INT intval =
(unsigned HOST_WIDE_INT) ((GET_CODE (operands[2]) == CONST_INT)
? INTVAL (operands[2]) : 0x55555555);
unsigned HOST_WIDE_INT det = (code != AND) ? intval : ~intval;
const char *opname;
char insn_buf[100];
switch (code)
{
case AND:
opname = "and";
break;
case IOR:
opname = "or";
break;
case XOR:
opname = "xor";
break;
default:
abort ();
}
switch (mode)
{
case HImode:
if ((TARGET_H8300H || TARGET_H8300S)
&& ((det & 0x00ff) != 0)
&& ((det & 0xff00) != 0))
{
sprintf (insn_buf, "%s.w\t%%T2,%%T0", opname);
output_asm_insn (insn_buf, operands);
}
else
{
if ((det & 0x00ff) != 0)
{
sprintf (insn_buf, "%s\t%%s2,%%s0", opname);
output_asm_insn (insn_buf, operands);
}
if ((det & 0xff00) != 0)
{
sprintf (insn_buf, "%s\t%%t2,%%t0", opname);
output_asm_insn (insn_buf, operands);
}
}
break;
case SImode:
if ((TARGET_H8300H || TARGET_H8300S)
&& ((det & 0x0000ffff) != 0)
&& ((det & 0xffff0000) != 0)
&& (code == IOR || det != 0xffffff00)
&& (code == IOR || det != 0xffff00ff))
{
sprintf (insn_buf, "%s.l\t%%S2,%%S0", opname);
output_asm_insn (insn_buf, operands);
}
else
{
if ((det & 0x0000ffff) == 0x0000ffff
&& (TARGET_H8300 ? (code == AND) : (code != IOR)))
output_asm_insn ((code == AND)
? "sub.w\t%f0,%f0" : "not.w\t%f0",
operands);
else if ((TARGET_H8300H || TARGET_H8300S)
&& ((det & 0x000000ff) != 0)
&& ((det & 0x0000ff00) != 0))
{
sprintf (insn_buf, "%s.w\t%%f2,%%f0", opname);
output_asm_insn (insn_buf, operands);
}
else
{
if ((det & 0x000000ff) != 0)
{
sprintf (insn_buf, "%s\t%%w2,%%w0", opname);
output_asm_insn (insn_buf, operands);
}
if ((det & 0x0000ff00) != 0)
{
sprintf (insn_buf, "%s\t%%x2,%%x0", opname);
output_asm_insn (insn_buf, operands);
}
}
if ((det & 0xffff0000) == 0xffff0000
&& (TARGET_H8300 ? (code == AND) : (code != IOR)))
output_asm_insn ((code == AND)
? "sub.w\t%e0,%e0" : "not.w\t%e0",
operands);
else if (TARGET_H8300H || TARGET_H8300S)
{
if ((det & 0xffff0000) != 0)
{
sprintf (insn_buf, "%s.w\t%%e2,%%e0", opname);
output_asm_insn (insn_buf, operands);
}
}
else
{
if ((det & 0x00ff0000) != 0)
{
sprintf (insn_buf, "%s\t%%y2,%%y0", opname);
output_asm_insn (insn_buf, operands);
}
if ((det & 0xff000000) != 0)
{
sprintf (insn_buf, "%s\t%%z2,%%z0", opname);
output_asm_insn (insn_buf, operands);
}
}
}
break;
default:
abort ();
}
return "";
}
unsigned int
compute_logical_op_length (mode, operands)
enum machine_mode mode;
rtx *operands;
{
enum rtx_code code = GET_CODE (operands[3]);
unsigned HOST_WIDE_INT intval =
(unsigned HOST_WIDE_INT) ((GET_CODE (operands[2]) == CONST_INT)
? INTVAL (operands[2]) : 0x55555555);
unsigned HOST_WIDE_INT det = (code != AND) ? intval : ~intval;
unsigned int length = 0;
switch (mode)
{
case HImode:
if ((TARGET_H8300H || TARGET_H8300S)
&& ((det & 0x00ff) != 0)
&& ((det & 0xff00) != 0))
{
if (REG_P (operands[2]))
length += 2;
else
length += 4;
}
else
{
if ((det & 0x00ff) != 0)
length += 2;
if ((det & 0xff00) != 0)
length += 2;
}
break;
case SImode:
if ((TARGET_H8300H || TARGET_H8300S)
&& ((det & 0x0000ffff) != 0)
&& ((det & 0xffff0000) != 0)
&& (code == IOR || det != 0xffffff00)
&& (code == IOR || det != 0xffff00ff))
{
if (REG_P (operands[2]))
length += 4;
else
length += 6;
}
else
{
if ((det & 0x0000ffff) == 0x0000ffff
&& (TARGET_H8300 ? (code == AND) : (code != IOR)))
{
length += 2;
}
else if ((TARGET_H8300H || TARGET_H8300S)
&& ((det & 0x000000ff) != 0)
&& ((det & 0x0000ff00) != 0))
{
length += 4;
}
else
{
if ((det & 0x000000ff) != 0)
length += 2;
if ((det & 0x0000ff00) != 0)
length += 2;
}
if ((det & 0xffff0000) == 0xffff0000
&& (TARGET_H8300 ? (code == AND) : (code != IOR)))
{
length += 2;
}
else if (TARGET_H8300H || TARGET_H8300S)
{
if ((det & 0xffff0000) != 0)
length += 4;
}
else
{
if ((det & 0x00ff0000) != 0)
length += 2;
if ((det & 0xff000000) != 0)
length += 2;
}
}
break;
default:
abort ();
}
return length;
}
int
compute_logical_op_cc (mode, operands)
enum machine_mode mode;
rtx *operands;
{
enum rtx_code code = GET_CODE (operands[3]);
unsigned HOST_WIDE_INT intval =
(unsigned HOST_WIDE_INT) ((GET_CODE (operands[2]) == CONST_INT)
? INTVAL (operands[2]) : 0x55555555);
unsigned HOST_WIDE_INT det = (code != AND) ? intval : ~intval;
enum attr_cc cc = CC_CLOBBER;
switch (mode)
{
case HImode:
if ((TARGET_H8300H || TARGET_H8300S)
&& ((det & 0x00ff) != 0)
&& ((det & 0xff00) != 0))
{
cc = CC_SET_ZNV;
}
break;
case SImode:
if ((TARGET_H8300H || TARGET_H8300S)
&& ((det & 0x0000ffff) != 0)
&& ((det & 0xffff0000) != 0)
&& (code == IOR || det != 0xffffff00)
&& (code == IOR || det != 0xffff00ff))
{
cc = CC_SET_ZNV;
}
break;
default:
abort ();
}
return cc;
}
int
nshift_operator (x, mode)
rtx x;
enum machine_mode mode ATTRIBUTE_UNUSED;
{
switch (GET_CODE (x))
{
case ASHIFTRT:
case LSHIFTRT:
case ASHIFT:
return 1;
default:
return 0;
}
}
int
expand_a_shift (mode, code, operands)
enum machine_mode mode;
int code;
rtx operands[];
{
emit_move_insn (operands[0], operands[1]);
emit_insn (gen_rtx_PARALLEL
(VOIDmode,
gen_rtvec (2,
gen_rtx_SET (VOIDmode, operands[0],
gen_rtx (code, mode, operands[0],
operands[2])),
gen_rtx_CLOBBER (VOIDmode,
gen_rtx_SCRATCH (QImode)))));
return 1;
}
enum shift_mode
{
QIshift, HIshift, SIshift
};
struct shift_insn
{
const char *const assembler;
const int cc_valid;
};
static const struct shift_insn shift_one[2][3][3] =
{
{
{
{ "shll\t%X0", CC_NO_CARRY },
{ "add.w\t%T0,%T0", CC_OVERFLOW_UNUSABLE | CC_NO_CARRY },
{ "add.w\t%f0,%f0\n\taddx\t%y0,%y0\n\taddx\t%z0,%z0", 0 }
},
{
{ "shlr\t%X0", CC_NO_CARRY },
{ "shlr\t%t0\n\trotxr\t%s0", 0 },
{ "shlr\t%z0\n\trotxr\t%y0\n\trotxr\t%x0\n\trotxr\t%w0", 0 }
},
{
{ "shar\t%X0", CC_OVERFLOW_UNUSABLE | CC_NO_CARRY },
{ "shar\t%t0\n\trotxr\t%s0", 0 },
{ "shar\t%z0\n\trotxr\t%y0\n\trotxr\t%x0\n\trotxr\t%w0", 0 }
}
},
{
{
{ "shll.b\t%X0", CC_NO_CARRY },
{ "shll.w\t%T0", CC_NO_CARRY },
{ "shll.l\t%S0", CC_NO_CARRY }
},
{
{ "shlr.b\t%X0", CC_NO_CARRY },
{ "shlr.w\t%T0", CC_NO_CARRY },
{ "shlr.l\t%S0", CC_NO_CARRY }
},
{
{ "shar.b\t%X0", CC_OVERFLOW_UNUSABLE | CC_NO_CARRY },
{ "shar.w\t%T0", CC_OVERFLOW_UNUSABLE | CC_NO_CARRY },
{ "shar.l\t%S0", CC_OVERFLOW_UNUSABLE | CC_NO_CARRY }
}
}
};
static const struct shift_insn shift_two[3][3] =
{
{
{ "shll.b\t#2,%X0", CC_NO_CARRY },
{ "shll.w\t#2,%T0", CC_NO_CARRY },
{ "shll.l\t#2,%S0", CC_NO_CARRY }
},
{
{ "shlr.b\t#2,%X0", CC_NO_CARRY },
{ "shlr.w\t#2,%T0", CC_NO_CARRY },
{ "shlr.l\t#2,%S0", CC_NO_CARRY }
},
{
{ "shar.b\t#2,%X0", CC_OVERFLOW_UNUSABLE | CC_NO_CARRY },
{ "shar.w\t#2,%T0", CC_OVERFLOW_UNUSABLE | CC_NO_CARRY },
{ "shar.l\t#2,%S0", CC_OVERFLOW_UNUSABLE | CC_NO_CARRY }
}
};
static const char *const rotate_one[2][3][3] =
{
{
{
"rotr\t%X0",
"shlr\t%t0\n\trotxr\t%s0\n\tbst\t#7,%t0",
0
},
{
"rotl\t%X0",
"shll\t%s0\n\trotxl\t%t0\n\tbst\t#0,%s0",
0
},
{
"rotl\t%X0",
"shll\t%s0\n\trotxl\t%t0\n\tbst\t#0,%s0",
0
}
},
{
{
"rotr.b\t%X0",
"rotr.w\t%T0",
"rotr.l\t%S0"
},
{
"rotl.b\t%X0",
"rotl.w\t%T0",
"rotl.l\t%S0"
},
{
"rotl.b\t%X0",
"rotl.w\t%T0",
"rotl.l\t%S0"
}
}
};
static const char *const rotate_two[3][3] =
{
{
"rotr.b\t#2,%X0",
"rotr.w\t#2,%T0",
"rotr.l\t#2,%S0"
},
{
"rotl.b\t#2,%X0",
"rotl.w\t#2,%T0",
"rotl.l\t#2,%S0"
},
{
"rotl.b\t#2,%X0",
"rotl.w\t#2,%T0",
"rotl.l\t#2,%S0"
}
};
struct shift_info {
enum shift_alg alg;
unsigned int remainder;
const char *special;
const char *shift1;
const char *shift2;
int cc_valid_p;
};
static void get_shift_alg PARAMS ((enum shift_type,
enum shift_mode, unsigned int,
struct shift_info *));
static void
get_shift_alg (shift_type, shift_mode, count, info)
enum shift_type shift_type;
enum shift_mode shift_mode;
unsigned int count;
struct shift_info *info;
{
enum h8_cpu cpu;
if (TARGET_H8300)
cpu = H8_300;
else if (TARGET_H8300H)
cpu = H8_300H;
else
cpu = H8_S;
info->alg = SHIFT_LOOP;
switch (shift_mode)
{
case QIshift:
if (count < GET_MODE_BITSIZE (QImode))
info->alg = shift_alg_qi[cpu][shift_type][count];
break;
case HIshift:
if (count < GET_MODE_BITSIZE (HImode))
info->alg = shift_alg_hi[cpu][shift_type][count];
break;
case SIshift:
if (count < GET_MODE_BITSIZE (SImode))
info->alg = shift_alg_si[cpu][shift_type][count];
break;
default:
abort ();
}
switch (info->alg)
{
case SHIFT_INLINE:
info->remainder = count;
case SHIFT_LOOP:
info->shift1 = shift_one[cpu_type][shift_type][shift_mode].assembler;
info->shift2 = shift_two[shift_type][shift_mode].assembler;
info->cc_valid_p = shift_one[cpu_type][shift_type][shift_mode].cc_valid;
goto end;
case SHIFT_ROT_AND:
info->shift1 = rotate_one[cpu_type][shift_type][shift_mode];
info->shift2 = rotate_two[shift_type][shift_mode];
info->cc_valid_p = 0;
goto end;
case SHIFT_SPECIAL:
info->remainder = 0;
info->shift1 = shift_one[cpu_type][shift_type][shift_mode].assembler;
info->shift2 = shift_two[shift_type][shift_mode].assembler;
info->cc_valid_p = 0;
break;
}
switch (shift_mode)
{
case QIshift:
if (shift_type == SHIFT_ASHIFTRT && count == 7)
{
info->special = "shll\t%X0\n\tsubx\t%X0,%X0";
goto end;
}
abort ();
case HIshift:
if (count == 7)
{
switch (shift_type)
{
case SHIFT_ASHIFT:
if (TARGET_H8300)
info->special = "shar.b\t%t0\n\tmov.b\t%s0,%t0\n\trotxr.b\t%t0\n\trotr.b\t%s0\n\tand.b\t#0x80,%s0";
else
info->special = "shar.b\t%t0\n\tmov.b\t%s0,%t0\n\trotxr.w\t%T0\n\tand.b\t#0x80,%s0";
goto end;
case SHIFT_LSHIFTRT:
if (TARGET_H8300)
info->special = "shal.b\t%s0\n\tmov.b\t%t0,%s0\n\trotxl.b\t%s0\n\trotl.b\t%t0\n\tand.b\t#0x01,%t0";
else
info->special = "shal.b\t%s0\n\tmov.b\t%t0,%s0\n\trotxl.w\t%T0\n\tand.b\t#0x01,%t0";
goto end;
case SHIFT_ASHIFTRT:
info->special = "shal.b\t%s0\n\tmov.b\t%t0,%s0\n\trotxl.b\t%s0\n\tsubx\t%t0,%t0";
goto end;
}
}
else if (8 <= count && count <= 13)
{
info->remainder = count - 8;
switch (shift_type)
{
case SHIFT_ASHIFT:
info->special = "mov.b\t%s0,%t0\n\tsub.b\t%s0,%s0";
info->shift1 = "shal.b\t%t0";
info->shift2 = "shal.b\t#2,%t0";
goto end;
case SHIFT_LSHIFTRT:
info->special = "mov.b\t%t0,%s0\n\tsub.b\t%t0,%t0";
info->shift1 = "shlr.b\t%s0";
info->shift2 = "shlr.b\t#2,%s0";
goto end;
case SHIFT_ASHIFTRT:
if (TARGET_H8300)
info->special = "mov.b\t%t0,%s0\n\tbld\t#7,%s0\n\tsubx\t%t0,%t0";
else
info->special = "mov.b\t%t0,%s0\n\texts.w\t%T0";
info->shift1 = "shar.b\t%s0";
info->shift2 = "shar.b\t#2,%s0";
goto end;
}
}
else if (count == 14)
{
switch (shift_type)
{
case SHIFT_ASHIFT:
if (TARGET_H8300)
info->special = "mov.b\t%s0,%t0\n\trotr.b\t%t0\n\trotr.b\t%t0\n\tand.b\t#0xC0,%t0\n\tsub.b\t%s0,%s0";
goto end;
case SHIFT_LSHIFTRT:
if (TARGET_H8300)
info->special = "mov.b\t%t0,%s0\n\trotl.b\t%s0\n\trotl.b\t%s0\n\tand.b\t#3,%s0\n\tsub.b\t%t0,%t0";
goto end;
case SHIFT_ASHIFTRT:
if (TARGET_H8300)
info->special = "mov.b\t%t0,%s0\n\tshll.b\t%s0\n\tsubx.b\t%t0,%t0\n\tshll.b\t%s0\n\tmov.b\t%t0,%s0\n\tbst.b\t#0,%s0";
else if (TARGET_H8300H)
info->special = "shll.b\t%t0\n\tsubx.b\t%s0,%s0\n\tshll.b\t%t0\n\trotxl.b\t%s0\n\texts.w\t%T0";
else
info->special = "mov.b\t%t0,%s0\n\texts.w\t%T0\n\tshar.w\t#2,%T0\n\tshar.w\t#2,%T0\n\tshar.w\t#2,%T0";
goto end;
}
}
else if (count == 15)
{
switch (shift_type)
{
case SHIFT_ASHIFT:
info->special = "bld\t#0,%s0\n\txor\t%s0,%s0\n\txor\t%t0,%t0\n\tbst\t#7,%t0";
goto end;
case SHIFT_LSHIFTRT:
info->special = "bld\t#7,%t0\n\txor\t%s0,%s0\n\txor\t%t0,%t0\n\tbst\t#0,%s0";
goto end;
case SHIFT_ASHIFTRT:
info->special = "shll\t%t0\n\tsubx\t%t0,%t0\n\tmov.b\t%t0,%s0";
goto end;
}
}
abort ();
case SIshift:
if (TARGET_H8300 && 8 <= count && count <= 9)
{
info->remainder = count - 8;
switch (shift_type)
{
case SHIFT_ASHIFT:
info->special = "mov.b\t%y0,%z0\n\tmov.b\t%x0,%y0\n\tmov.b\t%w0,%x0\n\tsub.b\t%w0,%w0";
goto end;
case SHIFT_LSHIFTRT:
info->special = "mov.b\t%x0,%w0\n\tmov.b\t%y0,%x0\n\tmov.b\t%z0,%y0\n\tsub.b\t%z0,%z0";
info->shift1 = "shlr\t%y0\n\trotxr\t%x0\n\trotxr\t%w0";
goto end;
case SHIFT_ASHIFTRT:
info->special = "mov.b\t%x0,%w0\n\tmov.b\t%y0,%x0\n\tmov.b\t%z0,%y0\n\tshll\t%z0\n\tsubx\t%z0,%z0";
goto end;
}
}
else if (count == 8 && !TARGET_H8300)
{
switch (shift_type)
{
case SHIFT_ASHIFT:
info->special = "mov.w\t%e0,%f4\n\tmov.b\t%s4,%t4\n\tmov.b\t%t0,%s4\n\tmov.b\t%s0,%t0\n\tsub.b\t%s0,%s0\n\tmov.w\t%f4,%e0";
goto end;
case SHIFT_LSHIFTRT:
info->special = "mov.w\t%e0,%f4\n\tmov.b\t%t0,%s0\n\tmov.b\t%s4,%t0\n\tmov.b\t%t4,%s4\n\textu.w\t%f4\n\tmov.w\t%f4,%e0";
goto end;
case SHIFT_ASHIFTRT:
info->special = "mov.w\t%e0,%f4\n\tmov.b\t%t0,%s0\n\tmov.b\t%s4,%t0\n\tmov.b\t%t4,%s4\n\texts.w\t%f4\n\tmov.w\t%f4,%e0";
goto end;
}
}
else if (count == 15 && TARGET_H8300)
{
switch (shift_type)
{
case SHIFT_ASHIFT:
abort ();
case SHIFT_LSHIFTRT:
info->special = "bld\t#7,%z0\n\tmov.w\t%e0,%f0\n\txor\t%y0,%y0\n\txor\t%z0,%z0\n\trotxl\t%w0,%w0\n\trotxl\t%x0,%x0\n\trotxl\t%y0,%y0";
goto end;
case SHIFT_ASHIFTRT:
info->special = "bld\t#7,%z0\n\tmov.w\t%e0,%f0\n\trotxl\t%w0,%w0\n\trotxl\t%x0,%x0\n\tsubx\t%y0,%y0\n\tsubx\t%z0,%z0";
goto end;
}
}
else if (count == 15 && !TARGET_H8300)
{
switch (shift_type)
{
case SHIFT_ASHIFT:
info->special = "shlr.w\t%e0\n\tmov.w\t%f0,%e0\n\txor.w\t%f0,%f0\n\trotxr.l\t%S0";
goto end;
case SHIFT_LSHIFTRT:
info->special = "shll.w\t%f0\n\tmov.w\t%e0,%f0\n\txor.w\t%e0,%e0\n\trotxl.l\t%S0";
goto end;
case SHIFT_ASHIFTRT:
abort ();
}
}
else if ((TARGET_H8300 && 16 <= count && count <= 20)
|| (TARGET_H8300H && 16 <= count && count <= 19)
|| (TARGET_H8300S && 16 <= count && count <= 21))
{
info->remainder = count - 16;
switch (shift_type)
{
case SHIFT_ASHIFT:
info->special = "mov.w\t%f0,%e0\n\tsub.w\t%f0,%f0";
if (TARGET_H8300)
{
info->shift1 = "add.w\t%e0,%e0";
}
else
{
info->shift1 = "shll.l\t%S0";
info->shift2 = "shll.l\t#2,%S0";
}
goto end;
case SHIFT_LSHIFTRT:
info->special = "mov.w\t%e0,%f0\n\tsub.w\t%e0,%e0";
if (TARGET_H8300)
{
info->shift1 = "shlr\t%x0\n\trotxr\t%w0";
}
else
{
info->shift1 = "shlr.l\t%S0";
info->shift2 = "shlr.l\t#2,%S0";
}
goto end;
case SHIFT_ASHIFTRT:
if (TARGET_H8300)
{
info->special = "mov.w\t%e0,%f0\n\tshll\t%z0\n\tsubx\t%z0,%z0\n\tmov.b\t%z0,%y0";
info->shift1 = "shar\t%x0\n\trotxr\t%w0";
}
else
{
info->special = "mov.w\t%e0,%f0\n\texts.l\t%S0";
info->shift1 = "shar.l\t%S0";
info->shift2 = "shar.l\t#2,%S0";
}
goto end;
}
}
else if (TARGET_H8300 && 24 <= count && count <= 28)
{
info->remainder = count - 24;
switch (shift_type)
{
case SHIFT_ASHIFT:
info->special = "mov.b\t%w0,%z0\n\tsub.b\t%y0,%y0\n\tsub.w\t%f0,%f0";
info->shift1 = "shll.b\t%z0";
goto end;
case SHIFT_LSHIFTRT:
info->special = "mov.b\t%z0,%w0\n\tsub.b\t%x0,%x0\n\tsub.w\t%e0,%e0";
info->shift1 = "shlr.b\t%w0";
goto end;
case SHIFT_ASHIFTRT:
info->special = "mov.b\t%z0,%w0\n\tbld\t#7,%w0\n\tsubx\t%x0,%x0\n\tsubx\t%x0,%x0\n\tsubx\t%x0,%x0";
info->shift1 = "shar.b\t%w0";
goto end;
}
}
else if ((TARGET_H8300H && count == 24)
|| (TARGET_H8300S && 24 <= count && count <= 25))
{
info->remainder = count - 24;
switch (shift_type)
{
case SHIFT_ASHIFT:
info->special = "mov.b\t%s0,%t0\n\tsub.b\t%s0,%s0\n\tmov.w\t%f0,%e0\n\tsub.w\t%f0,%f0";
info->shift1 = "shll.l\t%S0";
info->shift2 = "shll.l\t#2,%S0";
goto end;
case SHIFT_LSHIFTRT:
info->special = "mov.w\t%e0,%f0\n\tmov.b\t%t0,%s0\n\textu.w\t%f0\n\textu.l\t%S0";
info->shift1 = "shlr.l\t%S0";
info->shift2 = "shlr.l\t#2,%S0";
goto end;
case SHIFT_ASHIFTRT:
info->special = "mov.w\t%e0,%f0\n\tmov.b\t%t0,%s0\n\texts.w\t%f0\n\texts.l\t%S0";
info->shift1 = "shar.l\t%S0";
info->shift2 = "shar.l\t#2,%S0";
goto end;
}
}
else if (!TARGET_H8300 && count == 28)
{
switch (shift_type)
{
case SHIFT_ASHIFT:
if (TARGET_H8300H)
info->special = "sub.w\t%e0,%e0\n\trotr.l\t%S0\n\trotr.l\t%S0\n\trotr.l\t%S0\n\trotr.l\t%S0\n\tsub.w\t%f0,%f0";
else
info->special = "sub.w\t%e0,%e0\n\trotr.l\t#2,%S0\n\trotr.l\t#2,%S0\n\tsub.w\t%f0,%f0";
info->shift1 = "";
info->shift2 = "";
goto end;
case SHIFT_LSHIFTRT:
if (TARGET_H8300H)
info->special = "sub.w\t%f0,%f0\n\trotl.l\t%S0\n\trotl.l\t%S0\n\trotl.l\t%S0\n\trotl.l\t%S0\n\tsub.w\t%e0,%e0";
else
info->special = "sub.w\t%f0,%f0\n\trotl.l\t#2,%S0\n\trotl.l\t#2,%S0\n\tsub.w\t%e0,%e0";
info->shift1 = "";
info->shift2 = "";
goto end;
case SHIFT_ASHIFTRT:
abort ();
}
}
else if (!TARGET_H8300 && count == 29)
{
switch (shift_type)
{
case SHIFT_ASHIFT:
if (TARGET_H8300H)
info->special = "sub.w\t%e0,%e0\n\trotr.l\t%S0\n\trotr.l\t%S0\n\trotr.l\t%S0\n\tsub.w\t%f0,%f0";
else
info->special = "sub.w\t%e0,%e0\n\trotr.l\t#2,%S0\n\trotr.l\t%S0\n\tsub.w\t%f0,%f0";
info->shift1 = "";
info->shift2 = "";
goto end;
case SHIFT_LSHIFTRT:
if (TARGET_H8300H)
info->special = "sub.w\t%f0,%f0\n\trotl.l\t%S0\n\trotl.l\t%S0\n\trotl.l\t%S0\n\tsub.w\t%e0,%e0";
else
info->special = "sub.w\t%f0,%f0\n\trotl.l\t#2,%S0\n\trotl.l\t%S0\n\tsub.w\t%e0,%e0";
info->shift1 = "";
info->shift2 = "";
goto end;
case SHIFT_ASHIFTRT:
abort ();
}
}
else if (!TARGET_H8300 && count == 30)
{
switch (shift_type)
{
case SHIFT_ASHIFT:
if (TARGET_H8300H)
info->special = "sub.w\t%e0,%e0\n\trotr.l\t%S0\n\trotr.l\t%S0\n\tsub.w\t%f0,%f0";
else
info->special = "sub.w\t%e0,%e0\n\trotr.l\t#2,%S0\n\tsub.w\t%f0,%f0";
info->shift1 = "";
info->shift2 = "";
goto end;
case SHIFT_LSHIFTRT:
if (TARGET_H8300H)
info->special = "sub.w\t%f0,%f0\n\trotl.l\t%S0\n\trotl.l\t%S0\n\tsub.w\t%e0,%e0";
else
info->special = "sub.w\t%f0,%f0\n\trotl.l\t#2,%S0\n\tsub.w\t%e0,%e0";
info->shift1 = "";
info->shift2 = "";
goto end;
case SHIFT_ASHIFTRT:
abort ();
}
}
else if (count == 31)
{
if (TARGET_H8300)
{
switch (shift_type)
{
case SHIFT_ASHIFT:
info->special = "sub.w\t%e0,%e0\n\tshlr\t%w0\n\tmov.w\t%e0,%f0\n\trotxr\t%z0";
goto end;
case SHIFT_LSHIFTRT:
info->special = "sub.w\t%f0,%f0\n\tshll\t%z0\n\tmov.w\t%f0,%e0\n\trotxl\t%w0";
goto end;
case SHIFT_ASHIFTRT:
info->special = "shll\t%z0\n\tsubx\t%w0,%w0\n\tmov.b\t%w0,%x0\n\tmov.w\t%f0,%e0";
goto end;
}
}
else
{
switch (shift_type)
{
case SHIFT_ASHIFT:
info->special = "shlr.l\t%S0\n\txor.l\t%S0,%S0\n\trotxr.l\t%S0";
goto end;
case SHIFT_LSHIFTRT:
info->special = "shll.l\t%S0\n\txor.l\t%S0,%S0\n\trotxl.l\t%S0";
goto end;
case SHIFT_ASHIFTRT:
info->special = "shll\t%e0\n\tsubx\t%w0,%w0\n\tmov.b\t%w0,%x0\n\tmov.w\t%f0,%e0";
goto end;
}
}
}
abort ();
default:
abort ();
}
end:
if (!TARGET_H8300S)
info->shift2 = NULL;
}
int
h8300_shift_needs_scratch_p (count, mode)
int count;
enum machine_mode mode;
{
enum h8_cpu cpu;
int a, lr, ar;
if (GET_MODE_BITSIZE (mode) <= count)
return 1;
if (TARGET_H8300)
cpu = H8_300;
else if (TARGET_H8300H)
cpu = H8_300H;
else
cpu = H8_S;
switch (mode)
{
case QImode:
a = shift_alg_qi[cpu][SHIFT_ASHIFT][count];
lr = shift_alg_qi[cpu][SHIFT_LSHIFTRT][count];
ar = shift_alg_qi[cpu][SHIFT_ASHIFTRT][count];
break;
case HImode:
a = shift_alg_hi[cpu][SHIFT_ASHIFT][count];
lr = shift_alg_hi[cpu][SHIFT_LSHIFTRT][count];
ar = shift_alg_hi[cpu][SHIFT_ASHIFTRT][count];
break;
case SImode:
a = shift_alg_si[cpu][SHIFT_ASHIFT][count];
lr = shift_alg_si[cpu][SHIFT_LSHIFTRT][count];
ar = shift_alg_si[cpu][SHIFT_ASHIFTRT][count];
break;
default:
abort ();
}
return (a == SHIFT_LOOP || lr == SHIFT_LOOP || ar == SHIFT_LOOP
|| (!TARGET_H8300 && mode == SImode && count == 8));
}
const char *
output_a_shift (operands)
rtx *operands;
{
static int loopend_lab;
rtx shift = operands[3];
enum machine_mode mode = GET_MODE (shift);
enum rtx_code code = GET_CODE (shift);
enum shift_type shift_type;
enum shift_mode shift_mode;
struct shift_info info;
loopend_lab++;
switch (mode)
{
case QImode:
shift_mode = QIshift;
break;
case HImode:
shift_mode = HIshift;
break;
case SImode:
shift_mode = SIshift;
break;
default:
abort ();
}
switch (code)
{
case ASHIFTRT:
shift_type = SHIFT_ASHIFTRT;
break;
case LSHIFTRT:
shift_type = SHIFT_LSHIFTRT;
break;
case ASHIFT:
shift_type = SHIFT_ASHIFT;
break;
default:
abort ();
}
if (GET_CODE (operands[2]) != CONST_INT)
{
output_asm_insn ("mov.b %X2,%X4", operands);
fprintf (asm_out_file, "\tble .Lle%d\n", loopend_lab);
get_shift_alg (shift_type, shift_mode, 1, &info);
fprintf (asm_out_file, ".Llt%d:\n", loopend_lab);
output_asm_insn (info.shift1, operands);
output_asm_insn ("add #0xff,%X4", operands);
fprintf (asm_out_file, "\tbne .Llt%d\n", loopend_lab);
fprintf (asm_out_file, ".Lle%d:\n", loopend_lab);
return "";
}
else
{
int n = INTVAL (operands[2]);
if (n < 0)
n = 0;
else if ((unsigned int) n > GET_MODE_BITSIZE (mode))
n = GET_MODE_BITSIZE (mode);
get_shift_alg (shift_type, shift_mode, n, &info);
switch (info.alg)
{
case SHIFT_SPECIAL:
output_asm_insn (info.special, operands);
case SHIFT_INLINE:
n = info.remainder;
if (info.shift2 != NULL)
{
for (; n > 1; n -= 2)
output_asm_insn (info.shift2, operands);
}
for (; n > 0; n--)
output_asm_insn (info.shift1, operands);
if (info.cc_valid_p)
{
cc_status.value1 = operands[0];
cc_status.flags |= info.cc_valid_p;
}
return "";
case SHIFT_ROT_AND:
{
int m = GET_MODE_BITSIZE (mode) - n;
int mask = (shift_type == SHIFT_ASHIFT
? ((1 << m) - 1) << n
: (1 << m) - 1);
char insn_buf[200];
if (info.shift1 == 0)
abort ();
if (info.shift2 != NULL)
{
for (; m > 1; m -= 2)
output_asm_insn (info.shift2, operands);
}
for (; m > 0; m--)
output_asm_insn (info.shift1, operands);
if (TARGET_H8300)
{
switch (mode)
{
case QImode:
sprintf (insn_buf, "and\t#%d,%%X0", mask);
cc_status.value1 = operands[0];
cc_status.flags |= CC_NO_CARRY;
break;
case HImode:
sprintf (insn_buf, "and\t#%d,%%s0\n\tand\t#%d,%%t0",
mask & 255, mask >> 8);
break;
default:
abort ();
}
}
else
{
sprintf (insn_buf, "and.%c\t#%d,%%%c0",
"bwl"[shift_mode], mask,
mode == QImode ? 'X' : mode == HImode ? 'T' : 'S');
cc_status.value1 = operands[0];
cc_status.flags |= CC_NO_CARRY;
}
output_asm_insn (insn_buf, operands);
return "";
}
case SHIFT_LOOP:
if (info.shift2 != NULL)
{
fprintf (asm_out_file, "\tmov.b #%d,%sl\n", n / 2,
names_big[REGNO (operands[4])]);
fprintf (asm_out_file, ".Llt%d:\n", loopend_lab);
output_asm_insn (info.shift2, operands);
output_asm_insn ("add #0xff,%X4", operands);
fprintf (asm_out_file, "\tbne .Llt%d\n", loopend_lab);
if (n % 2)
output_asm_insn (info.shift1, operands);
}
else
{
fprintf (asm_out_file, "\tmov.b #%d,%sl\n", n,
names_big[REGNO (operands[4])]);
fprintf (asm_out_file, ".Llt%d:\n", loopend_lab);
output_asm_insn (info.shift1, operands);
output_asm_insn ("add #0xff,%X4", operands);
fprintf (asm_out_file, "\tbne .Llt%d\n", loopend_lab);
}
return "";
default:
abort ();
}
}
}
static unsigned int
h8300_asm_insn_count (template)
const char *template;
{
unsigned int count = 1;
for (; *template; template++)
if (*template == '\n')
count++;
return count;
}
unsigned int
compute_a_shift_length (insn, operands)
rtx insn ATTRIBUTE_UNUSED;
rtx *operands;
{
rtx shift = operands[3];
enum machine_mode mode = GET_MODE (shift);
enum rtx_code code = GET_CODE (shift);
enum shift_type shift_type;
enum shift_mode shift_mode;
struct shift_info info;
unsigned int wlength = 0;
switch (mode)
{
case QImode:
shift_mode = QIshift;
break;
case HImode:
shift_mode = HIshift;
break;
case SImode:
shift_mode = SIshift;
break;
default:
abort ();
}
switch (code)
{
case ASHIFTRT:
shift_type = SHIFT_ASHIFTRT;
break;
case LSHIFTRT:
shift_type = SHIFT_LSHIFTRT;
break;
case ASHIFT:
shift_type = SHIFT_ASHIFT;
break;
default:
abort ();
}
if (GET_CODE (operands[2]) != CONST_INT)
{
get_shift_alg (shift_type, shift_mode, 1, &info);
return (4 + h8300_asm_insn_count (info.shift1)) * 2;
}
else
{
int n = INTVAL (operands[2]);
if (n < 0)
n = 0;
else if ((unsigned int) n > GET_MODE_BITSIZE (mode))
n = GET_MODE_BITSIZE (mode);
get_shift_alg (shift_type, shift_mode, n, &info);
switch (info.alg)
{
case SHIFT_SPECIAL:
wlength += h8300_asm_insn_count (info.special);
if (strstr (info.special, "xor.l") != NULL)
wlength++;
case SHIFT_INLINE:
n = info.remainder;
if (info.shift2 != NULL)
{
wlength += h8300_asm_insn_count (info.shift2) * (n / 2);
n = n % 2;
}
wlength += h8300_asm_insn_count (info.shift1) * n;
return 2 * wlength;
case SHIFT_ROT_AND:
{
int m = GET_MODE_BITSIZE (mode) - n;
if (info.shift1 == 0)
abort ();
if (info.shift2 != NULL)
{
wlength += h8300_asm_insn_count (info.shift2) * (m / 2);
m = m % 2;
}
wlength += h8300_asm_insn_count (info.shift1) * m;
switch (mode)
{
case QImode:
wlength += 1;
break;
case HImode:
wlength += 2;
break;
case SImode:
if (TARGET_H8300)
abort ();
wlength += 3;
break;
default:
abort ();
}
return 2 * wlength;
}
case SHIFT_LOOP:
if (info.shift2 != NULL)
{
wlength += 3 + h8300_asm_insn_count (info.shift2);
if (n % 2)
wlength += h8300_asm_insn_count (info.shift1);
}
else
{
wlength += 3 + h8300_asm_insn_count (info.shift1);
}
return 2 * wlength;
default:
abort ();
}
}
}
int
expand_a_rotate (code, operands)
enum rtx_code code;
rtx operands[];
{
rtx dst = operands[0];
rtx src = operands[1];
rtx rotate_amount = operands[2];
enum machine_mode mode = GET_MODE (dst);
rtx tmp;
emit_move_insn (dst, src);
if (GET_CODE (rotate_amount) != CONST_INT)
{
rtx counter = gen_reg_rtx (QImode);
rtx start_label = gen_label_rtx ();
rtx end_label = gen_label_rtx ();
emit_cmp_and_jump_insns (rotate_amount, GEN_INT (0), LE, NULL_RTX,
QImode, 0, end_label);
emit_move_insn (counter, rotate_amount);
emit_label (start_label);
tmp = gen_rtx (code, mode, dst, GEN_INT (1));
emit_insn (gen_rtx_SET (mode, dst, tmp));
tmp = gen_rtx_PLUS (QImode, counter, GEN_INT (-1));
emit_insn (gen_rtx_SET (VOIDmode, counter, tmp));
emit_cmp_and_jump_insns (counter, GEN_INT (0), NE, NULL_RTX, QImode, 1,
start_label);
emit_label (end_label);
}
else
{
tmp = gen_rtx (code, mode, dst, rotate_amount);
emit_insn (gen_rtx_SET (mode, dst, tmp));
}
return 1;
}
const char *
emit_a_rotate (code, operands)
enum rtx_code code;
rtx *operands;
{
rtx dst = operands[0];
rtx rotate_amount = operands[2];
enum shift_mode rotate_mode;
enum shift_type rotate_type;
const char *insn_buf;
int bits;
int amount;
enum machine_mode mode = GET_MODE (dst);
if (GET_CODE (rotate_amount) != CONST_INT)
abort ();
switch (mode)
{
case QImode:
rotate_mode = QIshift;
break;
case HImode:
rotate_mode = HIshift;
break;
case SImode:
rotate_mode = SIshift;
break;
default:
abort ();
}
switch (code)
{
case ROTATERT:
rotate_type = SHIFT_ASHIFT;
break;
case ROTATE:
rotate_type = SHIFT_LSHIFTRT;
break;
default:
abort ();
}
amount = INTVAL (rotate_amount);
if (amount < 0)
amount = 0;
if ((unsigned int) amount > GET_MODE_BITSIZE (mode))
amount = GET_MODE_BITSIZE (mode);
if ((unsigned int) amount > GET_MODE_BITSIZE (mode) / (unsigned) 2)
{
amount = GET_MODE_BITSIZE (mode) - amount;
rotate_type =
(rotate_type == SHIFT_ASHIFT) ? SHIFT_LSHIFTRT : SHIFT_ASHIFT;
}
if ((mode == HImode && TARGET_H8300 && amount >= 5)
|| (mode == HImode && TARGET_H8300H && amount >= 6)
|| (mode == HImode && TARGET_H8300S && amount == 8)
|| (mode == SImode && TARGET_H8300H && amount >= 10)
|| (mode == SImode && TARGET_H8300S && amount >= 13))
{
switch (mode)
{
case HImode:
insn_buf = "xor.b\t%s0,%t0\n\txor.b\t%t0,%s0\n\txor.b\t%s0,%t0";
output_asm_insn (insn_buf, operands);
break;
case SImode:
insn_buf = "xor.w\t%e0,%f0\n\txor.w\t%f0,%e0\n\txor.w\t%e0,%f0";
output_asm_insn (insn_buf, operands);
break;
default:
abort ();
}
amount = GET_MODE_BITSIZE (mode) / 2 - amount;
rotate_type =
(rotate_type == SHIFT_ASHIFT) ? SHIFT_LSHIFTRT : SHIFT_ASHIFT;
}
for (bits = TARGET_H8300S ? 2 : 1; bits > 0; bits /= 2)
{
if (bits == 2)
insn_buf = rotate_two[rotate_type][rotate_mode];
else
insn_buf = rotate_one[cpu_type][rotate_type][rotate_mode];
for (; amount >= bits; amount -= bits)
output_asm_insn (insn_buf, operands);
}
return "";
}
int
fix_bit_operand (operands, what, type)
rtx *operands;
int what;
enum rtx_code type;
{
if ((what == 0 && single_zero_operand (operands[2], QImode))
|| (what == 1 && single_one_operand (operands[2], QImode)))
{
if (GET_CODE (operands[0]) == MEM
&& !EXTRA_CONSTRAINT (operands[0], 'U'))
{
rtx mem = gen_rtx_MEM (GET_MODE (operands[0]),
copy_to_mode_reg (Pmode,
XEXP (operands[0], 0)));
MEM_COPY_ATTRIBUTES (mem, operands[0]);
operands[0] = mem;
}
if (GET_CODE (operands[1]) == MEM
&& !EXTRA_CONSTRAINT (operands[1], 'U'))
{
rtx mem = gen_rtx_MEM (GET_MODE (operands[1]),
copy_to_mode_reg (Pmode,
XEXP (operands[1], 0)));
MEM_COPY_ATTRIBUTES (mem, operands[0]);
operands[1] = mem;
}
return 0;
}
operands[1] = force_reg (QImode, operands[1]);
{
rtx res = gen_reg_rtx (QImode);
emit_insn (gen_rtx_SET (VOIDmode, res,
gen_rtx (type, QImode, operands[1], operands[2])));
emit_insn (gen_rtx_SET (VOIDmode, operands[0], res));
}
return 1;
}
static int
h8300_interrupt_function_p (func)
tree func;
{
tree a;
if (TREE_CODE (func) != FUNCTION_DECL)
return 0;
a = lookup_attribute ("interrupt_handler", DECL_ATTRIBUTES (func));
return a != NULL_TREE;
}
static int
h8300_os_task_function_p (func)
tree func;
{
tree a;
if (TREE_CODE (func) != FUNCTION_DECL)
return 0;
a = lookup_attribute ("OS_Task", DECL_ATTRIBUTES (func));
return a != NULL_TREE;
}
static int
h8300_monitor_function_p (func)
tree func;
{
tree a;
if (TREE_CODE (func) != FUNCTION_DECL)
return 0;
a = lookup_attribute ("monitor", DECL_ATTRIBUTES (func));
return a != NULL_TREE;
}
int
h8300_funcvec_function_p (func)
tree func;
{
tree a;
if (TREE_CODE (func) != FUNCTION_DECL)
return 0;
a = lookup_attribute ("function_vector", DECL_ATTRIBUTES (func));
return a != NULL_TREE;
}
int
h8300_eightbit_data_p (decl)
tree decl;
{
tree a;
if (TREE_CODE (decl) != VAR_DECL)
return 0;
a = lookup_attribute ("eightbit_data", DECL_ATTRIBUTES (decl));
return a != NULL_TREE;
}
int
h8300_tiny_data_p (decl)
tree decl;
{
tree a;
if (TREE_CODE (decl) != VAR_DECL)
return 0;
a = lookup_attribute ("tiny_data", DECL_ATTRIBUTES (decl));
return a != NULL_TREE;
}
static void
h8300_insert_attributes (node, attributes)
tree node;
tree *attributes;
{
if (!interrupt_handler
|| TREE_CODE (node) != FUNCTION_DECL)
return;
*attributes = tree_cons (get_identifier ("interrupt_handler"),
NULL, *attributes);
}
const struct attribute_spec h8300_attribute_table[] =
{
{ "interrupt_handler", 0, 0, true, false, false, h8300_handle_fndecl_attribute },
{ "OS_Task", 0, 0, true, false, false, h8300_handle_fndecl_attribute },
{ "monitor", 0, 0, true, false, false, h8300_handle_fndecl_attribute },
{ "function_vector", 0, 0, true, false, false, h8300_handle_fndecl_attribute },
{ "eightbit_data", 0, 0, true, false, false, h8300_handle_eightbit_data_attribute },
{ "tiny_data", 0, 0, true, false, false, h8300_handle_tiny_data_attribute },
{ NULL, 0, 0, false, false, false, NULL }
};
static tree
h8300_handle_fndecl_attribute (node, name, args, flags, no_add_attrs)
tree *node;
tree name;
tree args ATTRIBUTE_UNUSED;
int flags ATTRIBUTE_UNUSED;
bool *no_add_attrs;
{
if (TREE_CODE (*node) != FUNCTION_DECL)
{
warning ("`%s' attribute only applies to functions",
IDENTIFIER_POINTER (name));
*no_add_attrs = true;
}
return NULL_TREE;
}
static tree
h8300_handle_eightbit_data_attribute (node, name, args, flags, no_add_attrs)
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 (7, ".eight");
}
else
{
warning ("`%s' attribute ignored", IDENTIFIER_POINTER (name));
*no_add_attrs = true;
}
return NULL_TREE;
}
static tree
h8300_handle_tiny_data_attribute (node, name, args, flags, no_add_attrs)
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, ".tiny");
}
else
{
warning ("`%s' attribute ignored", IDENTIFIER_POINTER (name));
*no_add_attrs = true;
}
return NULL_TREE;
}
static void
h8300_encode_label (decl)
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);
}
static void
h8300_encode_section_info (decl, first)
tree decl;
int first;
{
if (TREE_CODE (decl) == FUNCTION_DECL
&& h8300_funcvec_function_p (decl))
SYMBOL_REF_FLAG (XEXP (DECL_RTL (decl), 0)) = 1;
else if (TREE_CODE (decl) == VAR_DECL
&& (TREE_STATIC (decl) || DECL_EXTERNAL (decl)))
{
if (h8300_eightbit_data_p (decl))
SYMBOL_REF_FLAG (XEXP (DECL_RTL (decl), 0)) = 1;
else if (first && h8300_tiny_data_p (decl))
h8300_encode_label (decl);
}
}
static const char *
h8300_strip_name_encoding (str)
const char *str;
{
return str + (*str == '*' || *str == '@' || *str == '&');
}
const char *
output_simode_bld (bild, operands)
int bild;
rtx operands[];
{
if (TARGET_H8300)
{
output_asm_insn ("sub.w\t%e0,%e0\n\tsub.w\t%f0,%f0", operands);
if (bild)
output_asm_insn ("bild\t%Z2,%Y1", operands);
else
output_asm_insn ("bld\t%Z2,%Y1", operands);
output_asm_insn ("bst\t#0,%w0", operands);
}
else
{
if (bild)
output_asm_insn ("bild\t%Z2,%Y1", operands);
else
output_asm_insn ("bld\t%Z2,%Y1", operands);
output_asm_insn ("xor.l\t%S0,%S0\n\tbst\t#0,%w0", operands);
}
return "";
}
int
h8300_adjust_insn_length (insn, length)
rtx insn;
int length ATTRIBUTE_UNUSED;
{
rtx pat = PATTERN (insn);
if (GET_CODE (pat) == USE
|| GET_CODE (pat) == CLOBBER
|| GET_CODE (pat) == SEQUENCE
|| GET_CODE (pat) == ADDR_VEC
|| GET_CODE (pat) == ADDR_DIFF_VEC)
return 0;
if (get_attr_adjust_length (insn) == ADJUST_LENGTH_NO)
return 0;
if (GET_CODE (pat) == SET
&& (GET_CODE (SET_SRC (pat)) == MEM
|| GET_CODE (SET_DEST (pat)) == MEM))
{
rtx addr;
if (GET_CODE (SET_SRC (pat)) == MEM)
addr = XEXP (SET_SRC (pat), 0);
else
addr = XEXP (SET_DEST (pat), 0);
if (TARGET_H8300)
{
if (GET_CODE (addr) == REG)
return -2;
if (GET_MODE (SET_SRC (pat)) == QImode
&& h8300_eightbit_constant_address_p (addr))
return -2;
}
else
{
if (GET_CODE (addr) == REG)
return -6;
if (GET_CODE (addr) == PLUS
&& GET_CODE (XEXP (addr, 0)) == REG
&& GET_CODE (XEXP (addr, 1)) == CONST_INT
&& INTVAL (XEXP (addr, 1)) > -32768
&& INTVAL (XEXP (addr, 1)) < 32767)
return -4;
if (GET_MODE (SET_SRC (pat)) == QImode
&& h8300_eightbit_constant_address_p (addr))
return -6;
if (h8300_tiny_constant_address_p (addr))
return -4;
if (GET_CODE (addr) == CONST_INT)
return -2;
}
}
if (GET_CODE (pat) == SET
&& GET_CODE (SET_SRC (pat)) == CONST_INT
&& GET_MODE (SET_DEST (pat)) == SImode
&& INTVAL (SET_SRC (pat)) != 0)
{
int val = INTVAL (SET_SRC (pat));
if (TARGET_H8300
&& ((val & 0xffff) == 0
|| ((val >> 16) & 0xffff) == 0))
return -2;
if (TARGET_H8300H || TARGET_H8300S)
{
if (val == (val & 0xff)
|| val == (val & 0xff00))
return 4 - 6;
switch (val & 0xffffffff)
{
case 0xffffffff:
case 0xfffffffe:
case 0xfffffffc:
case 0x0000ffff:
case 0x0000fffe:
case 0xffff0000:
case 0xfffe0000:
case 0x00010000:
case 0x00020000:
return 4 - 6;
}
}
}
if (GET_CODE (pat) == SET
&& (GET_CODE (SET_SRC (pat)) == ROTATE
|| GET_CODE (SET_SRC (pat)) == ROTATERT))
{
rtx src = SET_SRC (pat);
enum machine_mode mode = GET_MODE (src);
int amount;
int states = 0;
if (GET_CODE (XEXP (src, 1)) != CONST_INT)
return 0;
amount = INTVAL (XEXP (src, 1));
if (amount < 0)
amount = 0;
if ((unsigned int) amount > GET_MODE_BITSIZE (mode))
amount = GET_MODE_BITSIZE (mode);
if ((unsigned int) amount > GET_MODE_BITSIZE (mode) / (unsigned) 2)
amount = GET_MODE_BITSIZE (mode) - amount;
if ((mode == HImode && TARGET_H8300 && amount >= 5)
|| (mode == HImode && TARGET_H8300H && amount >= 6)
|| (mode == HImode && TARGET_H8300S && amount == 8)
|| (mode == SImode && TARGET_H8300H && amount >= 10)
|| (mode == SImode && TARGET_H8300S && amount >= 13))
{
amount = GET_MODE_BITSIZE (mode) / 2 - amount;
states += 6;
}
if (TARGET_H8300S)
amount = amount / 2 + amount % 2;
states += amount * ((TARGET_H8300 && mode == HImode) ? 6 : 2);
return -(20 - states);
}
return 0;
}
#ifndef OBJECT_FORMAT_ELF
static void
h8300_asm_named_section (name, flags)
const char *name;
unsigned int flags ATTRIBUTE_UNUSED;
{
fprintf (asm_out_file, "\t.section %s\n", name);
}
#endif
int
h8300_eightbit_constant_address_p (x)
rtx x;
{
const unsigned HOST_WIDE_INT n1 = trunc_int_for_mode (0xff00, HImode);
const unsigned HOST_WIDE_INT n2 = trunc_int_for_mode (0xffff, HImode);
const unsigned HOST_WIDE_INT h1 = trunc_int_for_mode (0x00ffff00, SImode);
const unsigned HOST_WIDE_INT h2 = trunc_int_for_mode (0x00ffffff, SImode);
const unsigned HOST_WIDE_INT s1 = trunc_int_for_mode (0xffffff00, SImode);
const unsigned HOST_WIDE_INT s2 = trunc_int_for_mode (0xffffffff, SImode);
unsigned HOST_WIDE_INT addr;
if (GET_CODE (x) == SYMBOL_REF && SYMBOL_REF_FLAG (x))
return 1;
if (GET_CODE (x) != CONST_INT)
return 0;
addr = INTVAL (x);
return (0
|| ((TARGET_H8300 || TARGET_NORMAL_MODE) && IN_RANGE (addr, n1, n2))
|| (TARGET_H8300H && IN_RANGE (addr, h1, h2))
|| (TARGET_H8300S && IN_RANGE (addr, s1, s2)));
}
int
h8300_tiny_constant_address_p (x)
rtx x;
{
const unsigned HOST_WIDE_INT h1 = trunc_int_for_mode (0x00000000, SImode);
const unsigned HOST_WIDE_INT h2 = trunc_int_for_mode (0x00007fff, SImode);
const unsigned HOST_WIDE_INT h3 = trunc_int_for_mode (0x00ff8000, SImode);
const unsigned HOST_WIDE_INT h4 = trunc_int_for_mode (0x00ffffff, SImode);
const unsigned HOST_WIDE_INT s1 = trunc_int_for_mode (0x00000000, SImode);
const unsigned HOST_WIDE_INT s2 = trunc_int_for_mode (0x00007fff, SImode);
const unsigned HOST_WIDE_INT s3 = trunc_int_for_mode (0xffff8000, SImode);
const unsigned HOST_WIDE_INT s4 = trunc_int_for_mode (0xffffffff, SImode);
unsigned HOST_WIDE_INT addr;
if (GET_CODE (x) == SYMBOL_REF && TINY_DATA_NAME_P (XSTR (x, 0)))
return 1;
if (GET_CODE (x) != CONST_INT)
return 0;
addr = INTVAL (x);
return (0
|| ((TARGET_H8300H && !TARGET_NORMAL_MODE)
&& (IN_RANGE (addr, h1, h2) || IN_RANGE (addr, h3, h4)))
|| ((TARGET_H8300S && !TARGET_NORMAL_MODE)
&& (IN_RANGE (addr, s1, s2) || IN_RANGE (addr, s3, s4))));
}