#include "config.h"
#include "system.h"
+#include "coretypes.h"
+#include "tm.h"
#include "rtl.h"
#include "tree.h"
#include "regs.h"
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 *, const char *, unsigned int));
+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 *));
/* CPU_TYPE, says what cpu we're compiling for. */
int cpu_type;
#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;
\f
+/* See below where shifts are handled for explanation of this enum. */
+
+enum shift_alg
+{
+ SHIFT_INLINE,
+ SHIFT_ROT_AND,
+ SHIFT_SPECIAL,
+ SHIFT_LOOP
+};
+
+/* Symbols of the various shifts which can be used as indices. */
+
+enum shift_type
+{
+ SHIFT_ASHIFT, SHIFT_LSHIFTRT, SHIFT_ASHIFTRT
+};
+
+/* Macros to keep the shift algorithm tables small. */
+#define INL SHIFT_INLINE
+#define ROT SHIFT_ROT_AND
+#define LOP SHIFT_LOOP
+#define SPC SHIFT_SPECIAL
+
+/* The shift algorithms for each machine, mode, shift type, and shift
+ count are defined below. The three tables below correspond to
+ QImode, HImode, and SImode, respectively. Each table is organized
+ by, in the order of indecies, machine, shift type, and shift count. */
+
+static enum shift_alg shift_alg_qi[3][3][8] = {
+ {
+ /* TARGET_H8300 */
+ /* 0 1 2 3 4 5 6 7 */
+ { INL, INL, INL, INL, INL, ROT, ROT, ROT }, /* SHIFT_ASHIFT */
+ { INL, INL, INL, INL, INL, ROT, ROT, ROT }, /* SHIFT_LSHIFTRT */
+ { INL, INL, INL, INL, INL, LOP, LOP, SPC } /* SHIFT_ASHIFTRT */
+ },
+ {
+ /* TARGET_H8300H */
+ /* 0 1 2 3 4 5 6 7 */
+ { INL, INL, INL, INL, INL, ROT, ROT, ROT }, /* SHIFT_ASHIFT */
+ { INL, INL, INL, INL, INL, ROT, ROT, ROT }, /* SHIFT_LSHIFTRT */
+ { INL, INL, INL, INL, INL, LOP, LOP, SPC } /* SHIFT_ASHIFTRT */
+ },
+ {
+ /* TARGET_H8300S */
+ /* 0 1 2 3 4 5 6 7 */
+ { INL, INL, INL, INL, INL, INL, ROT, ROT }, /* SHIFT_ASHIFT */
+ { INL, INL, INL, INL, INL, INL, ROT, ROT }, /* SHIFT_LSHIFTRT */
+ { INL, INL, INL, INL, INL, INL, INL, SPC } /* SHIFT_ASHIFTRT */
+ }
+};
+
+static enum shift_alg shift_alg_hi[3][3][16] = {
+ {
+ /* TARGET_H8300 */
+ /* 0 1 2 3 4 5 6 7 */
+ /* 8 9 10 11 12 13 14 15 */
+ { INL, INL, INL, INL, INL, INL, INL, SPC,
+ SPC, SPC, SPC, SPC, SPC, SPC, SPC, SPC }, /* SHIFT_ASHIFT */
+ { INL, INL, INL, INL, INL, LOP, LOP, SPC,
+ SPC, SPC, SPC, SPC, SPC, SPC, SPC, SPC }, /* SHIFT_LSHIFTRT */
+ { INL, INL, INL, INL, INL, LOP, LOP, SPC,
+ SPC, SPC, SPC, SPC, SPC, SPC, SPC, SPC }, /* SHIFT_ASHIFTRT */
+ },
+ {
+ /* TARGET_H8300H */
+ /* 0 1 2 3 4 5 6 7 */
+ /* 8 9 10 11 12 13 14 15 */
+ { INL, INL, INL, INL, INL, INL, INL, SPC,
+ SPC, SPC, SPC, SPC, SPC, ROT, ROT, ROT }, /* SHIFT_ASHIFT */
+ { INL, INL, INL, INL, INL, INL, INL, SPC,
+ SPC, SPC, SPC, SPC, SPC, ROT, ROT, ROT }, /* SHIFT_LSHIFTRT */
+ { INL, INL, INL, INL, INL, INL, INL, SPC,
+ SPC, SPC, SPC, SPC, SPC, SPC, SPC, SPC }, /* SHIFT_ASHIFTRT */
+ },
+ {
+ /* TARGET_H8300S */
+ /* 0 1 2 3 4 5 6 7 */
+ /* 8 9 10 11 12 13 14 15 */
+ { INL, INL, INL, INL, INL, INL, INL, INL,
+ SPC, SPC, SPC, SPC, SPC, ROT, ROT, ROT }, /* SHIFT_ASHIFT */
+ { INL, INL, INL, INL, INL, INL, INL, INL,
+ SPC, SPC, SPC, SPC, SPC, ROT, ROT, ROT }, /* SHIFT_LSHIFTRT */
+ { INL, INL, INL, INL, INL, INL, INL, INL,
+ SPC, SPC, SPC, SPC, SPC, SPC, SPC, SPC }, /* SHIFT_ASHIFTRT */
+ }
+};
+
+static enum shift_alg shift_alg_si[3][3][32] = {
+ {
+ /* TARGET_H8300 */
+ /* 0 1 2 3 4 5 6 7 */
+ /* 8 9 10 11 12 13 14 15 */
+ /* 16 17 18 19 20 21 22 23 */
+ /* 24 25 26 27 28 29 30 31 */
+ { 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 }, /* SHIFT_ASHIFT */
+ { 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 }, /* SHIFT_LSHIFTRT */
+ { 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 }, /* SHIFT_ASHIFTRT */
+ },
+ {
+ /* TARGET_H8300H */
+ /* 0 1 2 3 4 5 6 7 */
+ /* 8 9 10 11 12 13 14 15 */
+ /* 16 17 18 19 20 21 22 23 */
+ /* 24 25 26 27 28 29 30 31 */
+ { 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 }, /* SHIFT_ASHIFT */
+ { 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 }, /* SHIFT_LSHIFTRT */
+ { 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 }, /* SHIFT_ASHIFTRT */
+ },
+ {
+ /* TARGET_H8300S */
+ /* 0 1 2 3 4 5 6 7 */
+ /* 8 9 10 11 12 13 14 15 */
+ /* 16 17 18 19 20 21 22 23 */
+ /* 24 25 26 27 28 29 30 31 */
+ { 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 }, /* SHIFT_ASHIFT */
+ { 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 }, /* SHIFT_LSHIFTRT */
+ { 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 }, /* SHIFT_ASHIFTRT */
+ }
+};
+
+#undef INL
+#undef ROT
+#undef LOP
+#undef SPC
+
+enum h8_cpu
+{
+ H8_300,
+ H8_300H,
+ H8_S
+};
+
/* Initialize various cpu specific globals at start up. */
void
}
else
{
- /* For this we treat the H8/300H and H8/S the same. */
+ /* For this we treat the H8/300H and H8S the same. */
cpu_type = (int) CPU_H8300H;
h8_reg_names = names_extended;
}
if (!TARGET_H8300S && TARGET_MAC)
{
error ("-ms2600 is used without -ms");
- target_flags |= 1;
+ target_flags |= MASK_H8300S;
+ }
+
+ if (TARGET_H8300 && TARGET_NORMAL_MODE)
+ {
+ error ("-mn is used without -mh or -ms");
+ target_flags ^= MASK_NORMAL_MODE;
+ }
+
+ /* Some of the shifts are optimized for speed by default.
+ See http://gcc.gnu.org/ml/gcc-patches/2002-07/msg01858.html
+ If optimizing for size, change shift_alg for those shift to
+ SHIFT_LOOP. */
+ if (optimize_size)
+ {
+ /* H8/300 */
+ 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;
+
+ /* H8/300H */
+ 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;
+
+ /* H8S */
+ shift_alg_hi[H8_S][SHIFT_ASHIFTRT][14] = SHIFT_LOOP;
}
}
SIZE to adjust the stack pointer. */
static void
-dosize (file, op, size)
+dosize (file, sign, size)
FILE *file;
- const char *op;
+ int sign;
unsigned int size;
{
- /* On the H8/300H and H8/S, for sizes <= 8 bytes, it is as good or
+ /* On the H8/300H and H8S, for sizes <= 8 bytes, it is as good or
better to use adds/subs insns rather than add.l/sub.l with an
immediate value.
|| ((TARGET_H8300H || TARGET_H8300S) && size <= 8)
|| (TARGET_H8300 && interrupt_handler)
|| (TARGET_H8300 && current_function_needs_context
- && ! strcmp (op, "sub")))
+ && sign < 0))
{
+ const char *op = (sign > 0) ? "add" : "sub";
unsigned HOST_WIDE_INT amount;
/* Try different amounts in descending order. */
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)
- fprintf (file, "\t%ss\t#%d,sp\n", op, amount);
+ fputs (insn, file);
}
}
else
{
if (TARGET_H8300)
- fprintf (file, "\tmov.w\t#%d,r3\n\t%s.w\tr3,sp\n", size, op);
+ {
+ fprintf (file, "\tmov.w\t#%d,r3\n\tadd.w\tr3,r7\n", sign * size);
+ }
else
- fprintf (file, "\t%s.l\t#%d,sp\n", op, size);
+ {
+ fprintf (file, "\tadd.l\t#%d,er7\n", sign * size);
+ }
}
}
round_frame_size (size)
int size;
{
- return (size + STACK_BOUNDARY / 8 - 1) & -STACK_BOUNDARY / 8;
+ return ((size + STACK_BOUNDARY / BITS_PER_UNIT - 1)
+ & -STACK_BOUNDARY / BITS_PER_UNIT);
}
/* Compute which registers to push/pop.
int regno;
/* Construct a bit vector of registers to be pushed/popped. */
- for (regno = 0; regno <= 6; regno++)
+ for (regno = 0; regno <= FRAME_POINTER_REGNUM; regno++)
{
if (WORD_REG_USED (regno))
saved_regs |= 1 << regno;
FILE *file;
int rn;
{
- fprintf (file, "\t%s\t%s\n", h8_push_op, h8_reg_names[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]);
}
/* Output assembly language code to pop register RN. */
FILE *file;
int rn;
{
- fprintf (file, "\t%s\t%s\n", h8_pop_op, h8_reg_names[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]);
}
-/* This is what the stack looks like after the prolog of
+/* This is what the stack looks like after the prolog of
a function with a frame has been set up:
<args>
PC
FP <- fp
<locals>
- <saved registers> <- sp
+ <saved registers> <- sp
This is what the stack looks like after the prolog of
a function which doesn't have a frame:
<args>
PC
<locals>
- <saved registers> <- sp
+ <saved registers> <- sp
*/
/* Output assembly language code for the function prologue. */
HOST_WIDE_INT size;
{
int fsize = round_frame_size (size);
- int idx;
+ int regno;
int saved_regs;
int n_regs;
}
/* Leave room for locals. */
- dosize (file, "sub", fsize);
+ dosize (file, -1, fsize);
/* Push the rest of the registers in ascending order. */
saved_regs = compute_saved_regs ();
- for (idx = 0; idx < FIRST_PSEUDO_REGISTER; idx += n_regs)
+ for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno += n_regs)
{
- int regno = idx;
-
n_regs = 1;
if (saved_regs & (1 << regno))
{
n_regs = 2;
}
- if (n_regs == 1)
- push (file, regno);
- else
- fprintf (file, "\tstm.l\t%s-%s,@-sp\n",
- h8_reg_names[regno],
- h8_reg_names[regno + (n_regs - 1)]);
+ 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 ();
+ }
}
}
}
HOST_WIDE_INT size;
{
int fsize = round_frame_size (size);
- int idx;
+ int regno;
rtx insn = get_last_insn ();
int saved_regs;
int n_regs;
/* Pop the saved registers in descending order. */
saved_regs = compute_saved_regs ();
- for (idx = 0; idx < FIRST_PSEUDO_REGISTER; idx += n_regs)
+ for (regno = FIRST_PSEUDO_REGISTER - 1; regno >= 0; regno -= n_regs)
{
- int regno = (FIRST_PSEUDO_REGISTER - 1) - idx;
-
n_regs = 1;
if (saved_regs & (1 << regno))
{
n_regs = 2;
}
- if (n_regs == 1)
- pop (file, regno);
- else
- fprintf (file, "\tldm.l\t@sp+,%s-%s\n",
- h8_reg_names[regno - (n_regs - 1)],
- h8_reg_names[regno]);
+ 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 ();
+ }
}
}
/* Deallocate locals. */
- dosize (file, "add", fsize);
+ dosize (file, 1, fsize);
/* Pop frame pointer if we had one. */
if (frame_pointer_needed)
{
fprintf (file, ";\tGCC For the Hitachi H8/300\n");
fprintf (file, ";\tBy Hitachi America Ltd and Cygnus Support\n");
- if (optimize)
+
+ 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");
fprintf (file, "\t.end\n");
}
\f
-/* Return true if VALUE is a valid constant for constraint 'P'.
- IE: VALUE is a power of two <= 2**15. */
-
-int
-small_power_of_two (value)
- HOST_WIDE_INT value;
-{
- int power = exact_log2 (value);
- return power >= 0 && power <= 15;
-}
-
-/* Return true if VALUE is a valid constant for constraint 'O', which
- means that the constant would be ok to use as a bit for a bclr
- instruction. */
-
-int
-ok_for_bclr (value)
- HOST_WIDE_INT value;
-{
- return small_power_of_two ((~value) & 0xff);
-}
-
/* Return true if OP is a valid source operand for an integer move
instruction. */
return general_operand (op, mode);
}
-/* Return true if OP is a const valid for a bit clear instruction. */
+/* Return true if OP is a constant that contains only one 1 in its
+ binary representation. */
int
-o_operand (operand, mode)
+single_one_operand (operand, mode)
rtx operand;
enum machine_mode mode ATTRIBUTE_UNUSED;
{
- return (GET_CODE (operand) == CONST_INT
- && CONST_OK_FOR_O (INTVAL (operand)));
+ if (GET_CODE (operand) == CONST_INT)
+ {
+ /* We really need to do this masking because 0x80 in QImode is
+ represented as -128 for example. */
+ 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;
}
-/* Return true if OP is a const valid for a bit set or bit xor instruction. */
+/* Return true if OP is a constant that contains only one 0 in its
+ binary representation. */
int
-p_operand (operand, mode)
+single_zero_operand (operand, mode)
rtx operand;
enum machine_mode mode ATTRIBUTE_UNUSED;
{
- return (GET_CODE (operand) == CONST_INT
- && CONST_OK_FOR_P (INTVAL (operand)));
+ if (GET_CODE (operand) == CONST_INT)
+ {
+ /* We really need to do this masking because 0x80 in QImode is
+ represented as -128 for example. */
+ 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;
}
/* Return true if OP is a valid call operand. */
return 0;
}
-/* Recognize valid operands for bitfield instructions. */
+/* Recognize valid operands for bit-field instructions. */
extern int rtx_equal_function_value_matters;
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);
+}
\f
/* Documentation for the machine specific operand escapes:
goto def;
break;
case 'V':
- bitint = exact_log2 (INTVAL (x));
+ bitint = exact_log2 (INTVAL (x) & 0xff);
if (bitint == -1)
abort ();
- fprintf (file, "#%d", bitint & 7);
+ fprintf (file, "#%d", bitint);
break;
case 'W':
bitint = exact_log2 ((~INTVAL (x)) & 0xff);
if (bitint == -1)
abort ();
- fprintf (file, "#%d", bitint & 7);
+ fprintf (file, "#%d", bitint);
break;
case 'R':
case 'X':
}
break;
case 'j':
- asm_fprintf (file, cond_string (GET_CODE (x)));
+ fputs (cond_string (GET_CODE (x)), file);
break;
case 'k':
- asm_fprintf (file, cond_string (reverse_condition (GET_CODE (x))));
+ fputs (cond_string (reverse_condition (GET_CODE (x))), file);
break;
case 's':
if (GET_CODE (x) == CONST_INT)
fprintf (file, "@");
output_address (addr);
- /* If this is an 'R' operand (reference into the 8-bit
- area), then specify a symbolic address as "foo:8",
- otherwise if operand is still in eight bit section, use
- "foo:16". */
- if (GET_CODE (addr) == SYMBOL_REF
- && SYMBOL_REF_FLAG (addr))
- fprintf (file, (code == 'R' ? ":8" : ":16"));
- else if (GET_CODE (addr) == SYMBOL_REF
- && TINY_DATA_NAME_P (XSTR (addr, 0)))
- fprintf (file, ":16");
- else if ((code == 'R')
- && EIGHTBIT_CONSTANT_ADDRESS_P (addr))
- fprintf (file, ":8");
+ /* We fall back from smaller addressing to larger
+ addressing in various ways depending on CODE. */
+ switch (code)
+ {
+ case 'R':
+ /* Used for mov.b and bit operations. */
+ if (h8300_eightbit_constant_address_p (addr))
+ {
+ fprintf (file, ":8");
+ break;
+ }
+
+ /* Fall through. We should not get here if we are
+ processing bit operations on H8/300 or H8/300H
+ because 'U' constraint does not allow bit
+ operations on the tiny area on these machines. */
+
+ case 'T':
+ case 'S':
+ /* Used for mov.w and mov.l. */
+ if (h8300_tiny_constant_address_p (addr))
+ fprintf (file, ":16");
+ break;
+ default:
+ break;
+ }
}
break;
the other its replacement, at the start of a routine. */
int
-initial_offset (from, to)
+h8300_initial_elimination_offset (from, to)
int from, to;
{
int offset = 0;
/* See the comments for get_frame_size. We need to round it up to
STACK_BOUNDARY. */
- offset += ((get_frame_size () + STACK_BOUNDARY / BITS_PER_UNIT - 1)
- & ~(STACK_BOUNDARY / BITS_PER_UNIT - 1));
+ offset += round_frame_size (get_frame_size ());
if (from == ARG_POINTER_REGNUM && to == STACK_POINTER_REGNUM)
offset += UNITS_PER_WORD; /* Skip saved PC */
}
+
+ if ((TARGET_H8300H || TARGET_H8300S) && TARGET_NORMAL_MODE)
+ offset -= 2;
+
return offset;
}
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_NO_CARRY;
cc_status.value1 = recog_data.operand[0];
+ if (GET_CODE (body) == SET && REG_P (SET_SRC (body)))
+ cc_status.value2 = SET_SRC (body);
break;
case CC_COMPARE:
}
\f
const char *
-output_logical_op (mode, code, operands)
+output_logical_op (mode, operands)
enum machine_mode mode;
- int code;
rtx *operands;
{
+ /* Figure out the logical op that we need to perform. */
+ enum rtx_code code = GET_CODE (operands[3]);
/* Pretend that every byte is affected if both operands are registers. */
unsigned HOST_WIDE_INT intval =
(unsigned HOST_WIDE_INT) ((GET_CODE (operands[2]) == CONST_INT)
}
return "";
}
-\f
-/* Shifts.
-
- We devote a fair bit of code to getting efficient shifts since we
- can only shift one bit at a time on the H8/300 and H8/300H and only
- one or two bits at a time on the H8/S.
-
- All shift code falls into one of the following ways of
- implementation:
- o SHIFT_INLINE: Emit straight line code for the shift; this is used
- when a straight line shift is about the same size or smaller than
- a loop.
+unsigned int
+compute_logical_op_length (mode, operands)
+ enum machine_mode mode;
+ rtx *operands;
+{
+ /* Figure out the logical op that we need to perform. */
+ enum rtx_code code = GET_CODE (operands[3]);
+ /* Pretend that every byte is affected if both operands are registers. */
+ unsigned HOST_WIDE_INT intval =
+ (unsigned HOST_WIDE_INT) ((GET_CODE (operands[2]) == CONST_INT)
+ ? INTVAL (operands[2]) : 0x55555555);
+ /* The determinant of the algorithm. If we perform an AND, 0
+ affects a bit. Otherwise, 1 affects a bit. */
+ unsigned HOST_WIDE_INT det = (code != AND) ? intval : ~intval;
+ /* Insn length. */
+ unsigned int length = 0;
- o SHIFT_ROT_AND: Rotate the value the opposite direction, then mask
+ switch (mode)
+ {
+ case HImode:
+ /* First, see if we can finish with one insn. */
+ if ((TARGET_H8300H || TARGET_H8300S)
+ && ((det & 0x00ff) != 0)
+ && ((det & 0xff00) != 0))
+ {
+ if (REG_P (operands[2]))
+ length += 2;
+ else
+ length += 4;
+ }
+ else
+ {
+ /* Take care of the lower byte. */
+ if ((det & 0x00ff) != 0)
+ length += 2;
+
+ /* Take care of the upper byte. */
+ if ((det & 0xff00) != 0)
+ length += 2;
+ }
+ break;
+ case SImode:
+ /* First, see if we can finish with one insn.
+
+ If code is either AND or XOR, we exclude two special cases,
+ 0xffffff00 and 0xffff00ff, because insns like sub.w or not.w
+ can do a better job. */
+ 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
+ {
+ /* Take care of the lower and upper words individually. For
+ each word, we try different methods in the order of
+
+ 1) the special insn (in case of AND or XOR),
+ 2) the word-wise insn, and
+ 3) The byte-wise insn. */
+ 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;
+{
+ /* Figure out the logical op that we need to perform. */
+ enum rtx_code code = GET_CODE (operands[3]);
+ /* Pretend that every byte is affected if both operands are registers. */
+ unsigned HOST_WIDE_INT intval =
+ (unsigned HOST_WIDE_INT) ((GET_CODE (operands[2]) == CONST_INT)
+ ? INTVAL (operands[2]) : 0x55555555);
+ /* The determinant of the algorithm. If we perform an AND, 0
+ affects a bit. Otherwise, 1 affects a bit. */
+ unsigned HOST_WIDE_INT det = (code != AND) ? intval : ~intval;
+ /* Condition code. */
+ enum attr_cc cc = CC_CLOBBER;
+
+ switch (mode)
+ {
+ case HImode:
+ /* First, see if we can finish with one insn. */
+ if ((TARGET_H8300H || TARGET_H8300S)
+ && ((det & 0x00ff) != 0)
+ && ((det & 0xff00) != 0))
+ {
+ cc = CC_SET_ZNV;
+ }
+ break;
+ case SImode:
+ /* First, see if we can finish with one insn.
+
+ If code is either AND or XOR, we exclude two special cases,
+ 0xffffff00 and 0xffff00ff, because insns like sub.w or not.w
+ can do a better job. */
+ 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;
+}
+\f
+/* Shifts.
+
+ We devote a fair bit of code to getting efficient shifts since we
+ can only shift one bit at a time on the H8/300 and H8/300H and only
+ one or two bits at a time on the H8S.
+
+ All shift code falls into one of the following ways of
+ implementation:
+
+ o SHIFT_INLINE: Emit straight line code for the shift; this is used
+ when a straight line shift is about the same size or smaller than
+ a loop.
+
+ o SHIFT_ROT_AND: Rotate the value the opposite direction, then mask
off the bits we don't need. This is used when only a few of the
bits in the original value will survive in the shifted value.
16. This case also includes other oddballs that are not worth
explaning here.
- o SHIFT_LOOP: Emit a loop using one (or two on H8/S) bit shifts.
-
- Here are some thoughts on what the absolutely positively best code
- is. "Best" here means some rational trade-off between code size
- and speed, where speed is more preferred but not at the expense of
- generating 20 insns.
-
- Below, a trailing '*' after the shift count indicates the "best"
- mode isn't implemented. We only describe SHIFT_SPECIAL cases to
- simplify the table. For other cases, refer to shift_alg_[qhs]i.
-
- H8/300 QImode shifts
- 7 - ASHIFTRT: shll, subx (propagate carry bit to all bits)
-
- H8/300 HImode shifts
- 7 - shift 2nd half other way into carry.
- copy 1st half into 2nd half
- rotate 2nd half other way with carry
- rotate 1st half other way (no carry)
- mask off bits in 1st half (ASHIFT | LSHIFTRT).
- sign extend 1st half (ASHIFTRT)
- 8 - move byte, zero (ASHIFT | LSHIFTRT) or sign extend other (ASHIFTRT)
- 9-12 - do shift by 8, inline remaining shifts
- 15 - ASHIFTRT: shll, subx, set other byte
-
- H8/300 SImode shifts
- 7* - shift other way once, move bytes into place,
- move carry into place (possibly with sign extension)
- 8 - move bytes into place, zero or sign extend other
- 15* - shift other way once, move word into place, move carry into place
- 16 - move word, zero or sign extend other
- 24* - move bytes into place, zero or sign extend other
- 31 - ASHIFTRT: shll top byte, subx, copy to other bytes
-
- H8/300H QImode shifts (same as H8/300 QImode shifts)
- 7 - ASHIFTRT: shll, subx (propagate carry bit to all bits)
-
- H8/300H HImode shifts
- 7 - shift 2nd half other way into carry.
- copy 1st half into 2nd half
- rotate entire word other way using carry
- mask off remaining bits (ASHIFT | LSHIFTRT)
- sign extend remaining bits (ASHIFTRT)
- 8 - move byte, zero (ASHIFT | LSHIFTRT) or sign extend other (ASHIFTRT)
- 9-12 - do shift by 8, inline remaining shifts
- 15 - ASHIFTRT: shll, subx, set other byte
-
- H8/300H SImode shifts
- (These are complicated by the fact that we don't have byte level access to
- the top word.)
- A word is: bytes 3,2,1,0 (msb -> lsb), word 1,0 (msw -> lsw)
- 15* - shift other way once, move word into place, move carry into place
- (with sign extension for ASHIFTRT)
- 16 - move word into place, zero or sign extend other
- 17-20 - do 16bit shift, then inline remaining shifts
- 24* - ASHIFT: move byte 0(msb) to byte 1, zero byte 0,
- move word 0 to word 1, zero word 0
- LSHIFTRT: move word 1 to word 0, move byte 1 to byte 0,
- zero word 1, zero byte 1
- ASHIFTRT: move word 1 to word 0, move byte 1 to byte 0,
- sign extend byte 0, sign extend word 0
- 25-27* - either loop, or
- do 24 bit shift, inline rest
- 31 - shll, subx byte 0, sign extend byte 0, sign extend word 0
-
- H8/S QImode shifts
- 7 - ASHIFTRT: shll, subx (propagate carry bit to all bits)
-
- H8/S HImode shifts
- 8 - move byte, zero (ASHIFT | LSHIFTRT) or sign extend other (ASHIFTRT)
- 9-12 - do shift by 8, inline remaining shifts
- 15 - ASHIFTRT: shll, subx, set other byte
-
- H8/S SImode shifts
- (These are complicated by the fact that we don't have byte level access to
- the top word.)
- A word is: bytes 3,2,1,0 (msb -> lsb), word 1,0 (msw -> lsw)
- 15* - shift other way once, move word into place, move carry into place
- (with sign extension for ASHIFTRT)
- 16 - move word into place, zero or sign extend other
- 17-20 - do 16bit shift, then inline remaining shifts
- 24* - ASHIFT: move byte 0(msb) to byte 1, zero byte 0,
- move word 0 to word 1, zero word 0
- LSHIFTRT: move word 1 to word 0, move byte 1 to byte 0,
- zero word 1, zero byte 1
- ASHIFTRT: move word 1 to word 0, move byte 1 to byte 0,
- sign extend byte 0, sign extend word 0
- 25-27* - either loop, or
- do 24 bit shift, inline rest
- 31 - shll, subx byte 0, sign extend byte 0, sign extend word 0
-
- Panic!!! */
+ o SHIFT_LOOP: Emit a loop using one (or two on H8S) bit shifts.
+
+ For each shift count, we try to use code that has no trade-off
+ between code size and speed whenever possible.
+
+ If the trade-off is unavoidable, we try to be reasonable.
+ Specifically, the fastest version is one instruction longer than
+ the shortest version, we take the fastest version. We also provide
+ the use a way to switch back to the shortest version with -Os.
+
+ For the details of the shift algorithms for various shift counts,
+ refer to shift_alg_[qhs]i. */
int
nshift_operator (x, mode)
return 1;
}
-/* See above for explanation of this enum. */
-
-enum shift_alg
-{
- SHIFT_INLINE,
- SHIFT_ROT_AND,
- SHIFT_SPECIAL,
- SHIFT_LOOP
-};
-
-/* Symbols of the various shifts which can be used as indices. */
-
-enum shift_type
-{
- SHIFT_ASHIFT, SHIFT_LSHIFTRT, SHIFT_ASHIFTRT
-};
-
/* Symbols of the various modes which can be used as indices. */
enum shift_mode
}
};
-/* Macros to keep the shift algorithm tables small. */
-#define INL SHIFT_INLINE
-#define ROT SHIFT_ROT_AND
-#define LOP SHIFT_LOOP
-#define SPC SHIFT_SPECIAL
-
-/* The shift algorithms for each machine, mode, shift type, and shift
- count are defined below. The three tables below correspond to
- QImode, HImode, and SImode, respectively. Each table is organized
- by, in the order of indecies, machine, shift type, and shift count. */
-
-static const enum shift_alg shift_alg_qi[3][3][8] = {
- {
- /* TARGET_H8300 */
- /* 0 1 2 3 4 5 6 7 */
- { INL, INL, INL, INL, INL, ROT, ROT, ROT }, /* SHIFT_ASHIFT */
- { INL, INL, INL, INL, INL, ROT, ROT, ROT }, /* SHIFT_LSHIFTRT */
- { INL, INL, INL, INL, INL, LOP, LOP, SPC } /* SHIFT_ASHIFTRT */
- },
- {
- /* TARGET_H8300H */
- /* 0 1 2 3 4 5 6 7 */
- { INL, INL, INL, INL, INL, ROT, ROT, ROT }, /* SHIFT_ASHIFT */
- { INL, INL, INL, INL, INL, ROT, ROT, ROT }, /* SHIFT_LSHIFTRT */
- { INL, INL, INL, INL, INL, LOP, LOP, SPC } /* SHIFT_ASHIFTRT */
- },
- {
- /* TARGET_H8300S */
- /* 0 1 2 3 4 5 6 7 */
- { INL, INL, INL, INL, INL, INL, ROT, ROT }, /* SHIFT_ASHIFT */
- { INL, INL, INL, INL, INL, INL, ROT, ROT }, /* SHIFT_LSHIFTRT */
- { INL, INL, INL, INL, INL, INL, INL, SPC } /* SHIFT_ASHIFTRT */
- }
-};
-
-static const enum shift_alg shift_alg_hi[3][3][16] = {
- {
- /* TARGET_H8300 */
- /* 0 1 2 3 4 5 6 7 */
- /* 8 9 10 11 12 13 14 15 */
- { INL, INL, INL, INL, INL, LOP, LOP, SPC,
- SPC, SPC, SPC, SPC, SPC, LOP, LOP, SPC }, /* SHIFT_ASHIFT */
- { INL, INL, INL, INL, INL, LOP, LOP, SPC,
- SPC, SPC, SPC, SPC, SPC, LOP, LOP, SPC }, /* SHIFT_LSHIFTRT */
- { INL, INL, INL, INL, INL, LOP, LOP, SPC,
- SPC, SPC, SPC, SPC, SPC, LOP, LOP, SPC }, /* SHIFT_ASHIFTRT */
- },
- {
- /* TARGET_H8300H */
- /* 0 1 2 3 4 5 6 7 */
- /* 8 9 10 11 12 13 14 15 */
- { INL, INL, INL, INL, INL, LOP, LOP, SPC,
- SPC, SPC, SPC, SPC, SPC, ROT, ROT, ROT }, /* SHIFT_ASHIFT */
- { INL, INL, INL, INL, INL, LOP, LOP, SPC,
- SPC, SPC, SPC, SPC, SPC, ROT, ROT, ROT }, /* SHIFT_LSHIFTRT */
- { INL, INL, INL, INL, INL, LOP, LOP, SPC,
- SPC, SPC, SPC, SPC, SPC, LOP, LOP, SPC }, /* SHIFT_ASHIFTRT */
- },
- {
- /* TARGET_H8300S */
- /* 0 1 2 3 4 5 6 7 */
- /* 8 9 10 11 12 13 14 15 */
- { INL, INL, INL, INL, INL, INL, INL, INL,
- SPC, SPC, SPC, SPC, SPC, ROT, ROT, ROT }, /* SHIFT_ASHIFT */
- { INL, INL, INL, INL, INL, INL, INL, INL,
- SPC, SPC, SPC, SPC, SPC, ROT, ROT, ROT }, /* SHIFT_LSHIFTRT */
- { INL, INL, INL, INL, INL, INL, INL, INL,
- SPC, SPC, SPC, SPC, SPC, LOP, LOP, SPC }, /* SHIFT_ASHIFTRT */
- }
-};
-
-static const enum shift_alg shift_alg_si[3][3][32] = {
- {
- /* TARGET_H8300 */
- /* 0 1 2 3 4 5 6 7 */
- /* 8 9 10 11 12 13 14 15 */
- /* 16 17 18 19 20 21 22 23 */
- /* 24 25 26 27 28 29 30 31 */
- { 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 }, /* SHIFT_ASHIFT */
- { 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 }, /* SHIFT_LSHIFTRT */
- { 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 }, /* SHIFT_ASHIFTRT */
- },
- {
- /* TARGET_H8300H */
- /* 0 1 2 3 4 5 6 7 */
- /* 8 9 10 11 12 13 14 15 */
- /* 16 17 18 19 20 21 22 23 */
- /* 24 25 26 27 28 29 30 31 */
- { 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, ROT, ROT, ROT, SPC }, /* SHIFT_ASHIFT */
- { 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, ROT, ROT, ROT, SPC }, /* SHIFT_LSHIFTRT */
- { 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 }, /* SHIFT_ASHIFTRT */
- },
- {
- /* TARGET_H8300S */
- /* 0 1 2 3 4 5 6 7 */
- /* 8 9 10 11 12 13 14 15 */
- /* 16 17 18 19 20 21 22 23 */
- /* 24 25 26 27 28 29 30 31 */
- { 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, ROT, ROT, ROT, SPC }, /* SHIFT_ASHIFT */
- { 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, ROT, ROT, ROT, SPC }, /* SHIFT_LSHIFTRT */
- { 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 }, /* SHIFT_ASHIFTRT */
- }
-};
-
-#undef INL
-#undef ROT
-#undef LOP
-#undef SPC
-
struct shift_info {
/* Shift algorithm. */
enum shift_alg alg;
const char *special;
/* Insn for a one-bit shift. Valid when ALG is either SHIFT_INLINE
- or SHIFT_SPECIAL, and REMAINDER is non-zero. */
+ or SHIFT_SPECIAL, and REMAINDER is nonzero. */
const char *shift1;
/* Insn for a two-bit shift. Valid when ALG is either SHIFT_INLINE
- or SHIFT_SPECIAL, and REMAINDER is non-zero. */
+ or SHIFT_SPECIAL, and REMAINDER is nonzero. */
const char *shift2;
/* Valid CC flags. */
/* Given SHIFT_TYPE, SHIFT_MODE, and shift count COUNT, determine the
best algorithm for doing the shift. The assembler code is stored
- in the pointers in INFO. We don't achieve maximum efficiency in
- all cases, but the hooks are here to do so.
-
- For now we just use lots of switch statements. Since we don't even come
- close to supporting all the cases, this is simplest. If this function ever
- gets too big, perhaps resort to a more table based lookup. Of course,
- at this point you may just wish to do it all in rtl.
+ in the pointers in INFO. We achieve the maximum efficiency in most
+ cases when !TARGET_H8300. In case of TARGET_H8300, shifts in
+ SImode in particular have a lot of room to optimize.
- WARNING: The constraints on insns shiftbyn_QI/HI/SI assume shifts of
- 1,2,3,4 will be inlined (1,2 for SI). */
+ We first determine the strategy of the shift algorithm by a table
+ lookup. If that tells us to use a hand crafted assembly code, we
+ go into the big switch statement to find what that is. Otherwise,
+ we resort to a generic way, such as inlining. In either case, the
+ result is returned through INFO. */
static void
get_shift_alg (shift_type, shift_mode, count, info)
unsigned int count;
struct shift_info *info;
{
- int cpu;
+ enum h8_cpu cpu;
/* Find the target CPU. */
if (TARGET_H8300)
- cpu = 0;
+ cpu = H8_300;
else if (TARGET_H8300H)
- cpu = 1;
+ cpu = H8_300H;
else
- cpu = 2;
+ cpu = H8_S;
/* Find the shift algorithm. */
+ info->alg = SHIFT_LOOP;
switch (shift_mode)
{
case QIshift:
- if (GET_MODE_BITSIZE (QImode) <= count)
- info->alg = SHIFT_LOOP;
- else
+ if (count < GET_MODE_BITSIZE (QImode))
info->alg = shift_alg_qi[cpu][shift_type][count];
break;
case HIshift:
- if (GET_MODE_BITSIZE (HImode) <= count)
- info->alg = SHIFT_LOOP;
- else
+ if (count < GET_MODE_BITSIZE (HImode))
info->alg = shift_alg_hi[cpu][shift_type][count];
break;
case SIshift:
- if (GET_MODE_BITSIZE (SImode) <= count)
- info->alg = SHIFT_LOOP;
- else
+ if (count < GET_MODE_BITSIZE (SImode))
info->alg = shift_alg_si[cpu][shift_type][count];
break;
goto end;
}
}
- else if (8 <= count && count <= 12)
+ else if (8 <= count && count <= 13)
{
info->remainder = count - 8;
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 /* TARGET_H8300S */
+ 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)
else if (TARGET_H8300 && 24 <= count && count <= 28)
{
info->remainder = count - 24;
-
+
switch (shift_type)
{
case SHIFT_ASHIFT:
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;
- }
- }
+ goto end;
+ }
+ }
else if ((TARGET_H8300H && count == 24)
|| (TARGET_H8300S && 24 <= count && count <= 25))
{
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";
+ 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)
info->shift2 = NULL;
}
+/* Given COUNT and MODE of a shift, return 1 if a scratch reg may be
+ needed for some shift with COUNT and MODE. Return 0 otherwise. */
+
+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;
+
+ /* Find out the target CPU. */
+ if (TARGET_H8300)
+ cpu = H8_300;
+ else if (TARGET_H8300H)
+ cpu = H8_300H;
+ else
+ cpu = H8_S;
+
+ /* Find the shift algorithm. */
+ 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 ();
+ }
+
+ /* On H8/300H and H8S, count == 8 uses the scratch register. */
+ return (a == SHIFT_LOOP || lr == SHIFT_LOOP || ar == SHIFT_LOOP
+ || (!TARGET_H8300 && mode == SImode && count == 8));
+}
+
/* Emit the assembler code for doing shifts. */
const char *
output_asm_insn (info.shift1, operands);
/* Now mask off the high bits. */
- if (TARGET_H8300)
+ switch (mode)
{
- 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:
+ if (TARGET_H8300)
{
- 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;
+ else
+ {
+ sprintf (insn_buf, "and.w\t#%d,%%T0", mask);
+ cc_status.value1 = operands[0];
+ cc_status.flags |= CC_NO_CARRY;
+ }
+ break;
+ default:
+ abort ();
}
output_asm_insn (insn_buf, operands);
return "";
}
}
}
+
+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 the assembler code to do one shift. */
+ 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 the count is negative, make it 0. */
+ if (n < 0)
+ n = 0;
+ /* If the count is too big, truncate it.
+ ANSI says shifts of GET_MODE_BITSIZE are undefined - we choose to
+ do the intuitive thing. */
+ 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);
+
+ /* Every assembly instruction used in SHIFT_SPECIAL case
+ takes 2 bytes except xor.l, which takes 4 bytes, so if we
+ see xor.l, we just pretend that xor.l counts as two insns
+ so that the insn length will be computed correctly. */
+ if (strstr (info.special, "xor.l") != NULL)
+ wlength++;
+
+ /* Fall through. */
+
+ 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;
+
+ /* Not all possibilities of rotate are supported. They shouldn't
+ be generated, but let's watch for 'em. */
+ 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;
+
+ /* Now mask off the high bits. */
+ 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:
+ /* A loop to shift by a "large" constant value.
+ If we have shift-by-2 insns, use them. */
+ 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 ();
+ }
+ }
+}
\f
/* A rotation by a non-constant will cause a loop to be generated, in
which a rotation by one bit is used. A rotation by a constant,
tmp = gen_rtx_PLUS (QImode, counter, GEN_INT (-1));
emit_insn (gen_rtx_SET (VOIDmode, counter, tmp));
- /* If the loop counter is non-zero, we go back to the beginning
+ /* If the loop counter is nonzero, we go back to the beginning
of the loop. */
emit_cmp_and_jump_insns (counter, GEN_INT (0), NE, NULL_RTX, QImode, 1,
start_label);
/* Determine the faster direction. After this phase, amount will be
at most a half of GET_MODE_BITSIZE (mode). */
- if ((unsigned int) amount > GET_MODE_BITSIZE (mode) / 2U)
+ if ((unsigned int) amount > GET_MODE_BITSIZE (mode) / (unsigned) 2)
{
/* Flip the direction. */
amount = GET_MODE_BITSIZE (mode) - amount;
break;
case SImode:
- /* This code works on the H8/300H and H8/S. */
+ /* This code works on the H8/300H and H8S. */
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;
only 'U' memory afterwards, so if this is a MEM operand, we must force
it to be valid for 'U' by reloading the address. */
- if (GET_CODE (operands[2]) == CONST_INT)
+ if ((what == 0 && single_zero_operand (operands[2], QImode))
+ || (what == 1 && single_one_operand (operands[2], QImode)))
{
- if (CONST_OK_FOR_LETTER_P (INTVAL (operands[2]), what))
+ /* OK to have a memory dest. */
+ if (GET_CODE (operands[0]) == MEM
+ && !EXTRA_CONSTRAINT (operands[0], 'U'))
{
- /* Ok to have a memory dest. */
- 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;
- }
+ 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;
+ 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;
}
/* Dest and src op must be register. */
return a != NULL_TREE;
}
+/* Generate an 'interrupt_handler' attribute for decls. */
+
+static void
+h8300_insert_attributes (node, attributes)
+ tree node;
+ tree *attributes;
+{
+ if (!interrupt_handler
+ || TREE_CODE (node) != FUNCTION_DECL)
+ return;
+
+ /* Add an 'interrupt_handler' attribute. */
+ *attributes = tree_cons (get_identifier ("interrupt_handler"),
+ NULL, *attributes);
+}
+
/* Supported attributes:
interrupt_handler: output a prologue and epilogue suitable for an
return NULL_TREE;
}
-void
+static void
h8300_encode_label (decl)
tree decl;
{
ggc_alloc_string (newstr, len + 1);
}
+/* If we are referencing a function that is supposed to be called
+ through the function vector, the SYMBOL_REF_FLAG in the rtl
+ so the call patterns can generate the correct code. */
+
+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);
+ }
+}
+
+/* Undo the effects of the above. */
+
+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[];
{
- /* Clear the destination register. */
- if (TARGET_H8300H || TARGET_H8300S)
- output_asm_insn ("sub.l\t%S0,%S0", operands);
- else
- output_asm_insn ("sub.w\t%e0,%e0\n\tsub.w\t%f0,%f0", operands);
+ if (TARGET_H8300)
+ {
+ /* Clear the destination register. */
+ output_asm_insn ("sub.w\t%e0,%e0\n\tsub.w\t%f0,%f0", operands);
- /* Now output the bit load or bit inverse load, and store it in
- the destination. */
- if (bild)
- output_asm_insn ("bild\t%Z2,%Y1\n\tbst\t#0,%w0", operands);
+ /* Now output the bit load or bit inverse load, and store it in
+ the destination. */
+ 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
- output_asm_insn ("bld\t%Z2,%Y1\n\tbst\t#0,%w0", operands);
+ {
+ /* Output the bit load or bit inverse load. */
+ if (bild)
+ output_asm_insn ("bild\t%Z2,%Y1", operands);
+ else
+ output_asm_insn ("bld\t%Z2,%Y1", operands);
+
+ /* Clear the destination register and perform the bit store. */
+ output_asm_insn ("xor.l\t%S0,%S0\n\tbst\t#0,%w0", operands);
+ }
/* All done. */
return "";
else
addr = XEXP (SET_DEST (pat), 0);
- /* On the H8/300, only one adjustment is necessary; if the
- address mode is register indirect, then this insn is two
- bytes shorter than indicated in the machine description. */
- if (TARGET_H8300 && GET_CODE (addr) == REG)
- return -2;
+ if (TARGET_H8300)
+ {
+ /* On the H8/300, we subtract the difference between the
+ actual length and the longest one, which is @(d:16,ERs). */
- /* On the H8/300H and H8/S, register indirect is 6 bytes shorter than
- indicated in the machine description. */
- if ((TARGET_H8300H || TARGET_H8300S)
- && GET_CODE (addr) == REG)
- return -6;
+ /* @Rs is 2 bytes shorter than the longest. */
+ if (GET_CODE (addr) == REG)
+ return -2;
- /* On the H8/300H and H8/S, reg + d, for small displacements is
- 4 bytes shorter than indicated in the machine description. */
- if ((TARGET_H8300H || TARGET_H8300S)
- && 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;
-
- /* On the H8/300H and H8/S, abs:16 is two bytes shorter than the
- more general abs:24. */
- if ((TARGET_H8300H || TARGET_H8300S)
- && GET_CODE (addr) == SYMBOL_REF
- && TINY_DATA_NAME_P (XSTR (addr, 0)))
- return -2;
+ /* @aa:8 is 2 bytes shorter than the longest. */
+ if (GET_MODE (SET_SRC (pat)) == QImode
+ && h8300_eightbit_constant_address_p (addr))
+ return -2;
+ }
+ else
+ {
+ /* On the H8/300H and H8S, we subtract the difference
+ between the actual length and the longest one, which is
+ @(d:24,ERs). */
+
+ /* @ERs is 6 bytes shorter than the longest. */
+ if (GET_CODE (addr) == REG)
+ return -6;
+
+ /* @(d:16,ERs) is 6 bytes shorter than the longest. */
+ 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;
+
+ /* @aa:8 is 6 bytes shorter than the longest. */
+ if (GET_MODE (SET_SRC (pat)) == QImode
+ && h8300_eightbit_constant_address_p (addr))
+ return -6;
+
+ /* @aa:16 is 4 bytes shorter than the longest. */
+ if (h8300_tiny_constant_address_p (addr))
+ return -4;
+
+ /* @aa:24 is 2 bytes shorter than the longest. */
+ if (GET_CODE (addr) == CONST_INT)
+ return -2;
+ }
}
/* Loading some constants needs adjustment. */
}
}
- /* Shifts need various adjustments. */
- if (GET_CODE (pat) == PARALLEL
- && GET_CODE (XVECEXP (pat, 0, 0)) == SET
- && (GET_CODE (SET_SRC (XVECEXP (pat, 0, 0))) == ASHIFTRT
- || GET_CODE (SET_SRC (XVECEXP (pat, 0, 0))) == LSHIFTRT
- || GET_CODE (SET_SRC (XVECEXP (pat, 0, 0))) == ASHIFT))
- {
- rtx src = SET_SRC (XVECEXP (pat, 0, 0));
- enum machine_mode mode = GET_MODE (src);
- int shift;
-
- if (GET_CODE (XEXP (src, 1)) != CONST_INT)
- return 0;
-
- shift = INTVAL (XEXP (src, 1));
- /* According to ANSI, negative shift is undefined. It is
- considered to be zero in this case (see function
- output_a_shift above). */
- if (shift < 0)
- shift = 0;
-
- /* QImode shifts by small constants take one insn
- per shift. So the adjustment is 20 (md length) -
- # shifts * 2. */
- if (mode == QImode && shift <= 4)
- return -(20 - shift * 2);
-
- /* Similarly for HImode and SImode shifts by small constants on
- the H8/300H and H8/S. */
- if ((TARGET_H8300H || TARGET_H8300S)
- && (mode == HImode || mode == SImode) && shift <= 4)
- return -(20 - shift * 2);
-
- /* HImode shifts by small constants for the H8/300. */
- if (mode == HImode && shift <= 4)
- return -(20 - (shift * (GET_CODE (src) == ASHIFT ? 2 : 4)));
-
- /* SImode shifts by small constants for the H8/300. */
- if (mode == SImode && shift <= 2)
- return -(20 - (shift * (GET_CODE (src) == ASHIFT ? 6 : 8)));
-
- /* XXX ??? Could check for more shift/rotate cases here. */
- }
-
/* Rotations need various adjustments. */
if (GET_CODE (pat) == SET
&& (GET_CODE (SET_SRC (pat)) == ROTATE
/* Determine the faster direction. After this phase, amount
will be at most a half of GET_MODE_BITSIZE (mode). */
- if ((unsigned int) amount > GET_MODE_BITSIZE (mode) / 2U)
+ if ((unsigned int) amount > GET_MODE_BITSIZE (mode) / (unsigned) 2)
/* Flip the direction. */
amount = GET_MODE_BITSIZE (mode) - amount;
states += 6;
}
- /* We use 2-bit rotatations on the H8/S. */
+ /* We use 2-bit rotatations on the H8S. */
if (TARGET_H8300S)
amount = amount / 2 + amount % 2;
fprintf (asm_out_file, "\t.section %s\n", name);
}
#endif /* ! OBJECT_FORMAT_ELF */
+
+/* Nonzero if X is a constant address suitable as an 8-bit absolute,
+ which is a special case of the 'R' operand. */
+
+int
+h8300_eightbit_constant_address_p (x)
+ rtx x;
+{
+ /* The ranges of the 8-bit area. */
+ 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;
+
+ /* We accept symbols declared with eightbit_data. */
+ 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)));
+}
+
+/* Nonzero if X is a constant address suitable as an 16-bit absolute
+ on H8/300H and H8S. */
+
+int
+h8300_tiny_constant_address_p (x)
+ rtx x;
+{
+ /* The ranges of the 16-bit area. */
+ 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;
+
+ /* We accept symbols declared with tiny_data. */
+ 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))));
+}