-/* Subroutines for insn-output.c for Hitachi H8/300.
+/* Subroutines for insn-output.c for Renesas H8/300.
Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000,
- 2001, 2002, 2003 Free Software Foundation, Inc.
+ 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
Contributed by Steve Chamberlain (sac@cygnus.com),
Jim Wilson (wilson@cygnus.com), and Doug Evans (dje@cygnus.com).
#include "recog.h"
#include "expr.h"
#include "function.h"
+#include "optabs.h"
#include "toplev.h"
#include "c-pragma.h"
#include "tm_p.h"
#include "target.h"
#include "target-def.h"
+/* Classifies a h8300_src_operand or h8300_dst_operand.
+
+ H8OP_IMMEDIATE
+ A constant operand of some sort.
+
+ H8OP_REGISTER
+ An ordinary register.
+
+ H8OP_MEM_ABSOLUTE
+ A memory reference with a constant address.
+
+ H8OP_MEM_BASE
+ A memory reference with a register as its address.
+
+ H8OP_MEM_COMPLEX
+ Some other kind of memory reference. */
+enum h8300_operand_class
+{
+ H8OP_IMMEDIATE,
+ H8OP_REGISTER,
+ H8OP_MEM_ABSOLUTE,
+ H8OP_MEM_BASE,
+ H8OP_MEM_COMPLEX,
+ NUM_H8OPS
+};
+
+/* For a general two-operand instruction, element [X][Y] gives
+ the length of the opcode fields when the first operand has class
+ (X + 1) and the second has class Y. */
+typedef unsigned char h8300_length_table[NUM_H8OPS - 1][NUM_H8OPS];
+
/* Forward declarations. */
-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 ((int, unsigned int));
-static int round_frame_size PARAMS ((int));
-static unsigned int compute_saved_regs PARAMS ((void));
-static void push PARAMS ((int));
-static void pop PARAMS ((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_epilogue PARAMS ((FILE *, HOST_WIDE_INT));
-static void h8300_insert_attributes PARAMS ((tree, tree *));
-static void h8300_file_end PARAMS ((void));
+static const char *byte_reg (rtx, int);
+static int h8300_interrupt_function_p (tree);
+static int h8300_saveall_function_p (tree);
+static int h8300_monitor_function_p (tree);
+static int h8300_os_task_function_p (tree);
+static void h8300_emit_stack_adjustment (int, unsigned int);
+static int round_frame_size (int);
+static unsigned int compute_saved_regs (void);
+static void push (int);
+static void pop (int);
+static const char *cond_string (enum rtx_code);
+static unsigned int h8300_asm_insn_count (const char *);
+static tree h8300_handle_fndecl_attribute (tree *, tree, tree, int, bool *);
+static tree h8300_handle_eightbit_data_attribute (tree *, tree, tree, int, bool *);
+static tree h8300_handle_tiny_data_attribute (tree *, tree, tree, int, bool *);
#ifndef OBJECT_FORMAT_ELF
-static void h8300_asm_named_section PARAMS ((const char *, unsigned int));
+static void h8300_asm_named_section (const char *, unsigned int, tree);
#endif
-static void h8300_encode_section_info PARAMS ((tree, rtx, int));
-static int const_costs PARAMS ((rtx, enum rtx_code, enum rtx_code));
-static int h8300_and_costs PARAMS ((rtx));
-static int h8300_shift_costs PARAMS ((rtx));
-static bool h8300_rtx_costs PARAMS ((rtx, int, int, int *));
+static int h8300_and_costs (rtx);
+static int h8300_shift_costs (rtx);
+static void h8300_push_pop (int, int, int, int);
+static int h8300_stack_offset_p (rtx, int);
+static int h8300_ldm_stm_regno (rtx, int, int, int);
+static void h8300_reorg (void);
+static unsigned int h8300_constant_length (rtx);
+static unsigned int h8300_displacement_length (rtx, int);
+static unsigned int h8300_classify_operand (rtx, int, enum h8300_operand_class *);
+static unsigned int h8300_length_from_table (rtx, rtx, const h8300_length_table *);
+static unsigned int h8300_unary_length (rtx);
+static unsigned int h8300_short_immediate_length (rtx);
+static unsigned int h8300_bitfield_length (rtx, rtx);
+static unsigned int h8300_binary_length (rtx, const h8300_length_table *);
+static bool h8300_short_move_mem_p (rtx, enum rtx_code);
+static unsigned int h8300_move_length (rtx *, const h8300_length_table *);
/* CPU_TYPE, says what cpu we're compiling for. */
int cpu_type;
const char *h8_push_op, *h8_pop_op, *h8_mov_op;
-/* Machine-specific symbol_ref flags. */
-#define SYMBOL_FLAG_FUNCVEC_FUNCTION (SYMBOL_FLAG_MACH_DEP << 0)
-#define SYMBOL_FLAG_EIGHTBIT_DATA (SYMBOL_FLAG_MACH_DEP << 1)
-#define SYMBOL_FLAG_TINY_DATA (SYMBOL_FLAG_MACH_DEP << 2)
-\f
-/* Initialize the GCC target structure. */
-#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_EPILOGUE
-#define TARGET_ASM_FUNCTION_EPILOGUE h8300_output_function_epilogue
-
-#undef TARGET_ASM_FILE_END
-#define TARGET_ASM_FILE_END h8300_file_end
-
-#undef TARGET_ENCODE_SECTION_INFO
-#define TARGET_ENCODE_SECTION_INFO h8300_encode_section_info
-
-#undef TARGET_INSERT_ATTRIBUTES
-#define TARGET_INSERT_ATTRIBUTES h8300_insert_attributes
-
-#undef TARGET_RTX_COSTS
-#define TARGET_RTX_COSTS h8300_rtx_costs
-
-struct gcc_target targetm = TARGET_INITIALIZER;
+/* Value of MOVE_RATIO. */
+int h8300_move_ratio;
\f
/* See below where shifts are handled for explanation of this enum. */
/* Initialize various cpu specific globals at start up. */
void
-h8300_init_once ()
+h8300_init_once (void)
{
static const char *const h8_push_ops[2] = { "push" , "push.l" };
static const char *const h8_pop_ops[2] = { "pop" , "pop.l" };
if (!TARGET_H8300S && TARGET_MAC)
{
error ("-ms2600 is used without -ms");
- target_flags |= MASK_H8300S;
+ target_flags |= MASK_H8300S_1;
}
if (TARGET_H8300 && TARGET_NORMAL_MODE)
/* H8S */
shift_alg_hi[H8_S][SHIFT_ASHIFTRT][14] = SHIFT_LOOP;
}
+
+ /* Work out a value for MOVE_RATIO. */
+ if (!TARGET_H8300SX)
+ {
+ /* Memory-memory moves are quite expensive without the
+ h8sx instructions. */
+ h8300_move_ratio = 3;
+ }
+ else if (flag_omit_frame_pointer)
+ {
+ /* movmd sequences are fairly cheap when er6 isn't fixed. They can
+ sometimes be as short as two individual memory-to-memory moves,
+ but since they use all the call-saved registers, it seems better
+ to allow up to three moves here. */
+ h8300_move_ratio = 4;
+ }
+ else if (optimize_size)
+ {
+ /* In this case we don't use movmd sequences since they tend
+ to be longer than calls to memcpy(). Memory-to-memory
+ moves are cheaper than for !TARGET_H8300SX, so it makes
+ sense to have a slightly higher threshold. */
+ h8300_move_ratio = 4;
+ }
+ else
+ {
+ /* We use movmd sequences for some moves since it can be quicker
+ than calling memcpy(). The sequences will need to save and
+ restore er6 though, so bump up the cost. */
+ h8300_move_ratio = 6;
+ }
+}
+
+/* Implement REG_CLASS_FROM_LETTER.
+
+ Some patterns need to use er6 as a scratch register. This is
+ difficult to arrange since er6 is the frame pointer and usually
+ can't be spilled.
+
+ Such patterns should define two alternatives, one which allows only
+ er6 and one which allows any general register. The former alternative
+ should have a 'd' constraint while the latter should be disparaged and
+ use 'D'.
+
+ Normally, 'd' maps to DESTINATION_REGS and 'D' maps to GENERAL_REGS.
+ However, there are cases where they should be NO_REGS:
+
+ - 'd' should be NO_REGS when reloading a function that uses the
+ frame pointer. In this case, DESTINATION_REGS won't contain any
+ spillable registers, so the first alternative can't be used.
+
+ - -fno-omit-frame-pointer means that the frame pointer will
+ always be in use. It's therefore better to map 'd' to NO_REGS
+ before reload so that register allocator will pick the second
+ alternative.
+
+ - we would like 'D' to be be NO_REGS when the frame pointer isn't
+ live, but we the frame pointer may turn out to be needed after
+ we start reload, and then we may have already decided we don't
+ have a choice, so we can't do that. Forcing the register
+ allocator to use er6 if possible might produce better code for
+ small functions: it's more efficient to save and restore er6 in
+ the prologue & epilogue than to do it in a define_split.
+ Hopefully disparaging 'D' will have a similar effect, without
+ forcing a reload failure if the frame pointer is found to be
+ needed too late. */
+
+enum reg_class
+h8300_reg_class_from_letter (int c)
+{
+ switch (c)
+ {
+ case 'a':
+ return MAC_REGS;
+
+ case 'c':
+ return COUNTER_REGS;
+
+ case 'd':
+ if (!flag_omit_frame_pointer && !reload_completed)
+ return NO_REGS;
+ if (frame_pointer_needed && reload_in_progress)
+ return NO_REGS;
+ return DESTINATION_REGS;
+
+ case 'D':
+ /* The meaning of a constraint shouldn't change dynamically, so
+ we can't make this NO_REGS. */
+ return GENERAL_REGS;
+
+ case 'f':
+ return SOURCE_REGS;
+
+ default:
+ return NO_REGS;
+ }
}
+/* Return the byte register name for a register rtx X. B should be 0
+ if you want a lower byte register. B should be 1 if you want an
+ upper byte register. */
+
static const char *
-byte_reg (x, b)
- rtx x;
- int b;
+byte_reg (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"
};
+ if (!REG_P (x))
+ abort ();
+
return names_small[REGNO (x) * 2 + b];
}
/* REGNO must be saved/restored across calls if this macro is true. */
#define WORD_REG_USED(regno) \
- (regno < 7 \
+ (regno < SP_REG \
/* No need to save registers if this function will not return. */ \
&& ! TREE_THIS_VOLATILE (current_function_decl) \
- && (pragma_saveall \
+ && (h8300_saveall_function_p (current_function_decl) \
/* Save any call saved register that was used. */ \
|| (regs_ever_live[regno] && !call_used_regs[regno]) \
/* Save the frame pointer if it was used. */ \
- || (regno == FRAME_POINTER_REGNUM && regs_ever_live[regno]) \
+ || (regno == HARD_FRAME_POINTER_REGNUM && regs_ever_live[regno]) \
/* Save any register used in an interrupt handler. */ \
|| (h8300_current_function_interrupt_function_p () \
&& regs_ever_live[regno]) \
SIZE to adjust the stack pointer. */
static void
-dosize (sign, size)
- int sign;
- unsigned int size;
+h8300_emit_stack_adjustment (int sign, unsigned int size)
{
+ /* If the frame size is 0, we don't have anything to do. */
+ if (size == 0)
+ return;
+
/* H8/300 cannot add/subtract a large constant with a single
instruction. If a temporary register is available, load the
constant to it and then do the addition. */
if (TARGET_H8300
&& size > 4
&& !h8300_current_function_interrupt_function_p ()
- && !(current_function_needs_context && sign < 0))
+ && !(cfun->static_chain_decl != NULL && sign < 0))
{
- rtx new_sp;
rtx r3 = gen_rtx_REG (Pmode, 3);
- emit_insn (gen_rtx_SET (Pmode, r3, GEN_INT (sign * size)));
- new_sp = gen_rtx_PLUS (Pmode, stack_pointer_rtx, r3);
- emit_insn (gen_rtx_SET (Pmode, stack_pointer_rtx, new_sp));
+ emit_insn (gen_movhi (r3, GEN_INT (sign * size)));
+ emit_insn (gen_addhi3 (stack_pointer_rtx,
+ stack_pointer_rtx, r3));
}
else
{
splitter. In case of H8/300, the splitter always splits the
addition emitted here to make the adjustment
interrupt-safe. */
- rtx new_sp = plus_constant (stack_pointer_rtx, sign * size);
- emit_insn (gen_rtx_SET (Pmode, stack_pointer_rtx, new_sp));
+ if (Pmode == HImode)
+ emit_insn (gen_addhi3 (stack_pointer_rtx,
+ stack_pointer_rtx, GEN_INT (sign * size)));
+ else
+ emit_insn (gen_addsi3 (stack_pointer_rtx,
+ stack_pointer_rtx, GEN_INT (sign * size)));
}
}
/* Round up frame size SIZE. */
static int
-round_frame_size (size)
- int size;
+round_frame_size (int size)
{
return ((size + STACK_BOUNDARY / BITS_PER_UNIT - 1)
& -STACK_BOUNDARY / BITS_PER_UNIT);
Return a bit vector of registers. */
static unsigned int
-compute_saved_regs ()
+compute_saved_regs (void)
{
unsigned int saved_regs = 0;
int regno;
/* Construct a bit vector of registers to be pushed/popped. */
- for (regno = 0; regno <= FRAME_POINTER_REGNUM; regno++)
+ for (regno = 0; regno <= HARD_FRAME_POINTER_REGNUM; regno++)
{
if (WORD_REG_USED (regno))
saved_regs |= 1 << regno;
/* Don't push/pop the frame pointer as it is treated separately. */
if (frame_pointer_needed)
- saved_regs &= ~(1 << FRAME_POINTER_REGNUM);
+ saved_regs &= ~(1 << HARD_FRAME_POINTER_REGNUM);
return saved_regs;
}
/* Emit an insn to push register RN. */
static void
-push (rn)
- int rn;
+push (int rn)
{
rtx reg = gen_rtx_REG (word_mode, rn);
rtx x;
if (TARGET_H8300)
x = gen_push_h8300 (reg);
+ else if (!TARGET_NORMAL_MODE)
+ x = gen_push_h8300hs_advanced (reg);
else
- x = gen_push_h8300hs (reg);
+ x = gen_push_h8300hs_normal (reg);
x = emit_insn (x);
REG_NOTES (x) = gen_rtx_EXPR_LIST (REG_INC, stack_pointer_rtx, 0);
}
/* Emit an insn to pop register RN. */
static void
-pop (rn)
- int rn;
+pop (int rn)
{
rtx reg = gen_rtx_REG (word_mode, rn);
rtx x;
if (TARGET_H8300)
x = gen_pop_h8300 (reg);
+ else if (!TARGET_NORMAL_MODE)
+ x = gen_pop_h8300hs_advanced (reg);
else
- x = gen_pop_h8300hs (reg);
+ x = gen_pop_h8300hs_normal (reg);
x = emit_insn (x);
REG_NOTES (x) = gen_rtx_EXPR_LIST (REG_INC, stack_pointer_rtx, 0);
}
+/* Emit an instruction to push or pop NREGS consecutive registers
+ starting at register REGNO. POP_P selects a pop rather than a
+ push and RETURN_P is true if the instruction should return.
+
+ It must be possible to do the requested operation in a single
+ instruction. If NREGS == 1 && !RETURN_P, use a normal push
+ or pop insn. Otherwise emit a parallel of the form:
+
+ (parallel
+ [(return) ;; if RETURN_P
+ (save or restore REGNO)
+ (save or restore REGNO + 1)
+ ...
+ (save or restore REGNO + NREGS - 1)
+ (set sp (plus sp (const_int adjust)))] */
+
+static void
+h8300_push_pop (int regno, int nregs, int pop_p, int return_p)
+{
+ int i, j;
+ rtvec vec;
+ rtx sp, offset;
+
+ /* See whether we can use a simple push or pop. */
+ if (!return_p && nregs == 1)
+ {
+ if (pop_p)
+ pop (regno);
+ else
+ push (regno);
+ return;
+ }
+
+ /* We need one element for the return insn, if present, one for each
+ register, and one for stack adjustment. */
+ vec = rtvec_alloc ((return_p != 0) + nregs + 1);
+ sp = stack_pointer_rtx;
+ i = 0;
+
+ /* Add the return instruction. */
+ if (return_p)
+ {
+ RTVEC_ELT (vec, i) = gen_rtx_RETURN (VOIDmode);
+ i++;
+ }
+
+ /* Add the register moves. */
+ for (j = 0; j < nregs; j++)
+ {
+ rtx lhs, rhs;
+
+ if (pop_p)
+ {
+ /* Register REGNO + NREGS - 1 is popped first. Before the
+ stack adjustment, its slot is at address @sp. */
+ lhs = gen_rtx_REG (SImode, regno + j);
+ rhs = gen_rtx_MEM (SImode, plus_constant (sp, (nregs - j - 1) * 4));
+ }
+ else
+ {
+ /* Register REGNO is pushed first and will be stored at @(-4,sp). */
+ lhs = gen_rtx_MEM (SImode, plus_constant (sp, (j + 1) * -4));
+ rhs = gen_rtx_REG (SImode, regno + j);
+ }
+ RTVEC_ELT (vec, i + j) = gen_rtx_SET (VOIDmode, lhs, rhs);
+ }
+
+ /* Add the stack adjustment. */
+ offset = GEN_INT ((pop_p ? nregs : -nregs) * 4);
+ RTVEC_ELT (vec, i + j) = gen_rtx_SET (VOIDmode, sp,
+ gen_rtx_PLUS (Pmode, sp, offset));
+
+ emit_insn (gen_rtx_PARALLEL (VOIDmode, vec));
+}
+
+/* Return true if X has the value sp + OFFSET. */
+
+static int
+h8300_stack_offset_p (rtx x, int offset)
+{
+ if (offset == 0)
+ return x == stack_pointer_rtx;
+
+ return (GET_CODE (x) == PLUS
+ && XEXP (x, 0) == stack_pointer_rtx
+ && GET_CODE (XEXP (x, 1)) == CONST_INT
+ && INTVAL (XEXP (x, 1)) == offset);
+}
+
+/* A subroutine of h8300_ldm_stm_parallel. X is one pattern in
+ something that may be an ldm or stm instruction. If it fits
+ the required template, return the register it loads or stores,
+ otherwise return -1.
+
+ LOAD_P is true if X should be a load, false if it should be a store.
+ NREGS is the number of registers that the whole instruction is expected
+ to load or store. INDEX is the index of the register that X should
+ load or store, relative to the lowest-numbered register. */
+
+static int
+h8300_ldm_stm_regno (rtx x, int load_p, int index, int nregs)
+{
+ int regindex, memindex, offset;
+
+ if (load_p)
+ regindex = 0, memindex = 1, offset = (nregs - index - 1) * 4;
+ else
+ memindex = 0, regindex = 1, offset = (index + 1) * -4;
+
+ if (GET_CODE (x) == SET
+ && GET_CODE (XEXP (x, regindex)) == REG
+ && GET_CODE (XEXP (x, memindex)) == MEM
+ && h8300_stack_offset_p (XEXP (XEXP (x, memindex), 0), offset))
+ return REGNO (XEXP (x, regindex));
+
+ return -1;
+}
+
+/* Return true if the elements of VEC starting at FIRST describe an
+ ldm or stm instruction (LOAD_P says which). */
+
+int
+h8300_ldm_stm_parallel (rtvec vec, int load_p, int first)
+{
+ rtx last;
+ int nregs, i, regno, adjust;
+
+ /* There must be a stack adjustment, a register move, and at least one
+ other operation (a return or another register move). */
+ if (GET_NUM_ELEM (vec) < 3)
+ return false;
+
+ /* Get the range of registers to be pushed or popped. */
+ nregs = GET_NUM_ELEM (vec) - first - 1;
+ regno = h8300_ldm_stm_regno (RTVEC_ELT (vec, first), load_p, 0, nregs);
+
+ /* Check that the call to h8300_ldm_stm_regno succeeded and
+ that we're only dealing with GPRs. */
+ if (regno < 0 || regno + nregs > 8)
+ return false;
+
+ /* 2-register h8s instructions must start with an even-numbered register.
+ 3- and 4-register instructions must start with er0 or er4. */
+ if (!TARGET_H8300SX)
+ {
+ if ((regno & 1) != 0)
+ return false;
+ if (nregs > 2 && (regno & 3) != 0)
+ return false;
+ }
+
+ /* Check the other loads or stores. */
+ for (i = 1; i < nregs; i++)
+ if (h8300_ldm_stm_regno (RTVEC_ELT (vec, first + i), load_p, i, nregs)
+ != regno + i)
+ return false;
+
+ /* Check the stack adjustment. */
+ last = RTVEC_ELT (vec, first + nregs);
+ adjust = (load_p ? nregs : -nregs) * 4;
+ return (GET_CODE (last) == SET
+ && SET_DEST (last) == stack_pointer_rtx
+ && h8300_stack_offset_p (SET_SRC (last), adjust));
+}
+
/* This is what the stack looks like after the prolog of
a function with a frame has been set up:
/* Generate RTL code for the function prologue. */
void
-h8300_expand_prologue ()
+h8300_expand_prologue (void)
{
int regno;
int saved_regs;
if (frame_pointer_needed)
{
/* Push fp. */
- push (FRAME_POINTER_REGNUM);
- emit_insn (gen_rtx_SET (Pmode, frame_pointer_rtx, stack_pointer_rtx));
+ push (HARD_FRAME_POINTER_REGNUM);
+ emit_move_insn (hard_frame_pointer_rtx, stack_pointer_rtx);
}
- /* Leave room for locals. */
- dosize (-1, round_frame_size (get_frame_size ()));
-
/* Push the rest of the registers in ascending order. */
saved_regs = compute_saved_regs ();
for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno += n_regs)
if (TARGET_H8300S)
{
/* See how many registers we can push at the same time. */
- if ((regno == 0 || regno == 4)
+ if ((!TARGET_H8300SX || (regno & 3) == 0)
&& ((saved_regs >> regno) & 0x0f) == 0x0f)
n_regs = 4;
- else if ((regno == 0 || regno == 4)
+ else if ((!TARGET_H8300SX || (regno & 3) == 0)
&& ((saved_regs >> regno) & 0x07) == 0x07)
n_regs = 3;
- else if ((regno == 0 || regno == 2 || regno == 4 || regno == 6)
+ else if ((!TARGET_H8300SX || (regno & 1) == 0)
&& ((saved_regs >> regno) & 0x03) == 0x03)
n_regs = 2;
}
- switch (n_regs)
- {
- case 1:
- push (regno);
- break;
- case 2:
- emit_insn (gen_stm_h8300s_2 (gen_rtx_REG (SImode, regno),
- gen_rtx_REG (SImode, regno + 1)));
- break;
- case 3:
- emit_insn (gen_stm_h8300s_3 (gen_rtx_REG (SImode, regno),
- gen_rtx_REG (SImode, regno + 1),
- gen_rtx_REG (SImode, regno + 2)));
- break;
- case 4:
- emit_insn (gen_stm_h8300s_4 (gen_rtx_REG (SImode, regno),
- gen_rtx_REG (SImode, regno + 1),
- gen_rtx_REG (SImode, regno + 2),
- gen_rtx_REG (SImode, regno + 3)));
- break;
- default:
- abort ();
- }
+ h8300_push_pop (regno, n_regs, 0, 0);
}
}
+
+ /* Leave room for locals. */
+ h8300_emit_stack_adjustment (-1, round_frame_size (get_frame_size ()));
}
+/* Return nonzero if we can use "rts" for the function currently being
+ compiled. */
+
int
-h8300_can_use_return_insn_p ()
+h8300_can_use_return_insn_p (void)
{
return (reload_completed
&& !frame_pointer_needed
/* Generate RTL code for the function epilogue. */
void
-h8300_expand_epilogue ()
+h8300_expand_epilogue (void)
{
int regno;
int saved_regs;
int n_regs;
+ HOST_WIDE_INT frame_size;
+ bool returned_p;
if (h8300_os_task_function_p (current_function_decl))
/* OS_Task epilogues are nearly naked -- they just have an
rts instruction. */
return;
+ frame_size = round_frame_size (get_frame_size ());
+ returned_p = false;
+
+ /* Deallocate locals. */
+ h8300_emit_stack_adjustment (1, frame_size);
+
/* Pop the saved registers in descending order. */
saved_regs = compute_saved_regs ();
for (regno = FIRST_PSEUDO_REGISTER - 1; regno >= 0; regno -= n_regs)
if (TARGET_H8300S)
{
/* See how many registers we can pop at the same time. */
- if ((regno == 7 || regno == 3)
- && ((saved_regs >> (regno - 3)) & 0x0f) == 0x0f)
+ if ((TARGET_H8300SX || (regno & 3) == 3)
+ && ((saved_regs << 3 >> regno) & 0x0f) == 0x0f)
n_regs = 4;
- else if ((regno == 6 || regno == 2)
- && ((saved_regs >> (regno - 2)) & 0x07) == 0x07)
+ else if ((TARGET_H8300SX || (regno & 3) == 2)
+ && ((saved_regs << 2 >> regno) & 0x07) == 0x07)
n_regs = 3;
- else if ((regno == 7 || regno == 5 || regno == 3 || regno == 1)
- && ((saved_regs >> (regno - 1)) & 0x03) == 0x03)
+ else if ((TARGET_H8300SX || (regno & 1) == 1)
+ && ((saved_regs << 1 >> regno) & 0x03) == 0x03)
n_regs = 2;
}
- switch (n_regs)
- {
- case 1:
- pop (regno);
- break;
- case 2:
- emit_insn (gen_ldm_h8300s_2 (gen_rtx_REG (SImode, regno - 1),
- gen_rtx_REG (SImode, regno)));
- break;
- case 3:
- emit_insn (gen_ldm_h8300s_3 (gen_rtx_REG (SImode, regno - 2),
- gen_rtx_REG (SImode, regno - 1),
- gen_rtx_REG (SImode, regno)));
- break;
- case 4:
- emit_insn (gen_ldm_h8300s_4 (gen_rtx_REG (SImode, regno - 3),
- gen_rtx_REG (SImode, regno - 2),
- gen_rtx_REG (SImode, regno - 1),
- gen_rtx_REG (SImode, regno)));
- break;
- default:
- abort ();
- }
+ /* See if this pop would be the last insn before the return.
+ If so, use rte/l or rts/l instead of pop or ldm.l. */
+ if (TARGET_H8300SX
+ && !frame_pointer_needed
+ && frame_size == 0
+ && (saved_regs & ((1 << (regno - n_regs + 1)) - 1)) == 0)
+ returned_p = true;
+
+ h8300_push_pop (regno - n_regs + 1, n_regs, 1, returned_p);
}
}
- /* Deallocate locals. */
- dosize (1, round_frame_size (get_frame_size ()));
-
/* Pop frame pointer if we had one. */
if (frame_pointer_needed)
- pop (FRAME_POINTER_REGNUM);
-}
-
-/* Output assembly language code for the function epilogue. */
+ {
+ if (TARGET_H8300SX)
+ returned_p = true;
+ h8300_push_pop (HARD_FRAME_POINTER_REGNUM, 1, 1, returned_p);
+ }
-static void
-h8300_output_function_epilogue (file, size)
- FILE *file ATTRIBUTE_UNUSED;
- HOST_WIDE_INT size ATTRIBUTE_UNUSED;
-{
- pragma_saveall = 0;
+ if (!returned_p)
+ emit_insn (gen_rtx_RETURN (VOIDmode));
}
-
+
/* Return nonzero if the current function is an interrupt
function. */
int
-h8300_current_function_interrupt_function_p ()
+h8300_current_function_interrupt_function_p (void)
{
return (h8300_interrupt_function_p (current_function_decl)
|| h8300_monitor_function_p (current_function_decl));
/* Output assembly code for the start of the file. */
-void
-asm_file_start (file)
- FILE *file;
+static void
+h8300_file_start (void)
{
- fprintf (file, ";\tGCC For the Hitachi H8/300\n");
- fprintf (file, ";\tBy Hitachi America Ltd and Cygnus Support\n");
+ default_file_start ();
- if (optimize_size)
- fprintf (file, "; -Os\n");
- else if (optimize)
- fprintf (file, "; -O%d\n", optimize);
if (TARGET_H8300H)
- if (TARGET_NORMAL_MODE)
- fprintf (file, "\n\t.h8300hn\n");
- else
- fprintf (file, "\n\t.h8300h\n");
+ fputs (TARGET_NORMAL_MODE ? "\t.h8300hn\n" : "\t.h8300h\n", asm_out_file);
+ else if (TARGET_H8300SX)
+ fputs (TARGET_NORMAL_MODE ? "\t.h8300sxn\n" : "\t.h8300sx\n", asm_out_file);
else if (TARGET_H8300S)
- if (TARGET_NORMAL_MODE)
- fprintf (file, "\n\t.h8300sn\n");
- else
- fprintf (file, "\n\t.h8300s\n");
- else
- fprintf (file, "\n\n");
- output_file_directive (file, main_input_filename);
+ fputs (TARGET_NORMAL_MODE ? "\t.h8300sn\n" : "\t.h8300s\n", asm_out_file);
}
/* Output assembly language code for the end of file. */
static void
-h8300_file_end ()
+h8300_file_end (void)
{
fputs ("\t.end\n", asm_out_file);
}
\f
-/* Return true if OP is a valid source operand for an integer move
- instruction. */
-
-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);
-}
-
-/* Return true if OP is a valid destination operand for an integer move
- instruction. */
-
-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);
-}
-
-/* Return true if OP is a constant that contains only one 1 in its
- binary representation. */
-
-int
-single_one_operand (operand, mode)
- rtx operand;
- enum machine_mode mode ATTRIBUTE_UNUSED;
-{
- if (GET_CODE (operand) == CONST_INT)
- {
- /* We really need to do this masking because 0x80 in QImode is
- represented as -128 for example. */
- if (exact_log2 (INTVAL (operand) & GET_MODE_MASK (mode)) >= 0)
- return 1;
- }
-
- return 0;
-}
-
-/* Return true if OP is a constant that contains only one 0 in its
- binary representation. */
-
-int
-single_zero_operand (operand, mode)
- rtx operand;
- enum machine_mode mode ATTRIBUTE_UNUSED;
-{
- if (GET_CODE (operand) == CONST_INT)
- {
- /* We really need to do this masking because 0x80 in QImode is
- represented as -128 for example. */
- if (exact_log2 (~INTVAL (operand) & GET_MODE_MASK (mode)) >= 0)
- return 1;
- }
-
- return 0;
-}
-
-/* Return true if OP is a valid call operand. */
-
-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;
-}
-
-/* Return 1 if an addition/subtraction of a constant integer can be
- transformed into two consecutive adds/subs that are faster than the
- straightforward way. Otherwise, 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);
-
- /* Force VALUE to be positive so that we do not have to consider
- the negative case. */
- if (value < 0)
- value = -value;
- if (TARGET_H8300H || TARGET_H8300S)
- {
- /* A constant addition/subtraction takes 2 states in QImode,
- 4 states in HImode, and 6 states in SImode. Thus, the
- only case we can win is when SImode is used, in which
- case, two adds/subs are used, taking 4 states. */
- if (mode == SImode
- && (value == 2 + 1
- || value == 4 + 1
- || value == 4 + 2
- || value == 4 + 4))
- return 1;
- }
- else
- {
- /* We do not profit directly by splitting addition or
- subtraction of 3 and 4. However, since these are
- implemented as a sequence of adds or subs, they do not
- clobber (cc0) unlike a sequence of add.b and add.x. */
- if (mode == HImode
- && (value == 2 + 1
- || value == 2 + 2))
- return 1;
- }
- }
-
- return 0;
-}
-
/* Split an add of a small constant into two adds/subs insns.
If USE_INCDEC_P is nonzero, we generate the last insn using inc/dec
instead of adds/subs. */
void
-split_adds_subs (mode, operands)
- enum machine_mode mode;
- rtx *operands;
+split_adds_subs (enum machine_mode mode, rtx *operands)
{
HOST_WIDE_INT val = INTVAL (operands[1]);
rtx reg = operands[0];
return;
}
-/* Return true if OP is a valid call operand, and OP represents
- an operand for a small call (4 bytes instead of 6 bytes). */
-
-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);
-
- /* Register indirect is a small call. */
- if (register_operand (inside, Pmode))
- return 1;
-
- /* A call through the function vector is a small call too. */
- if (GET_CODE (inside) == SYMBOL_REF
- && (SYMBOL_REF_FLAGS (inside) & SYMBOL_FLAG_FUNCVEC_FUNCTION))
- return 1;
- }
- /* Otherwise it's a large call. */
- return 0;
-}
-
-/* Return true if OP is a valid jump operand. */
-
-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;
-}
-
-/* Recognize valid operands for bit-field instructions. */
-
-extern int rtx_equal_function_value_matters;
-
-int
-bit_operand (op, mode)
- rtx op;
- enum machine_mode mode;
-{
- /* We can accept any general operand, except that MEM operands must
- be limited to those that use addresses valid for the 'U' constraint. */
- if (!general_operand (op, mode))
- return 0;
-
- /* Accept any mem during RTL generation. Otherwise, the code that does
- insv and extzv will think that we can not handle memory. However,
- to avoid reload problems, we only accept 'U' MEM operands after RTL
- generation. This means that any named pattern which uses this predicate
- must force its operands to match 'U' before emitting RTL. */
-
- if (GET_CODE (op) == REG)
- return 1;
- if (GET_CODE (op) == SUBREG)
- return 1;
- 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'));
-}
-
/* Handle machine specific pragmas for compatibility with existing
compilers for the H8/300.
with this attribute may be safely used in an interrupt vector. */
void
-h8300_pr_interrupt (pfile)
- struct cpp_reader *pfile ATTRIBUTE_UNUSED;
+h8300_pr_interrupt (struct cpp_reader *pfile ATTRIBUTE_UNUSED)
{
pragma_interrupt = 1;
}
void
-h8300_pr_saveall (pfile)
- struct cpp_reader *pfile ATTRIBUTE_UNUSED;
+h8300_pr_saveall (struct cpp_reader *pfile ATTRIBUTE_UNUSED)
{
pragma_saveall = 1;
}
If the argument is to be pushed, NULL_RTX is returned. */
rtx
-function_arg (cum, mode, type, named)
- CUMULATIVE_ARGS *cum;
- enum machine_mode mode;
- tree type;
- int named;
+function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode,
+ tree type, int named)
{
static const char *const hand_list[] = {
"__main",
return result;
}
\f
-/* Return the cost of the rtx R with code CODE. */
+/* Compute the cost of an and insn. */
static int
-const_costs (r, c, outer_code)
- rtx r;
- enum rtx_code c;
- enum rtx_code outer_code;
+h8300_and_costs (rtx x)
{
- switch (c)
- {
- case CONST_INT:
- {
- HOST_WIDE_INT n = INTVAL (r);
-
- if (-4 <= n || n <= 4)
- {
- switch ((int) n)
- {
- 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;
- }
- }
- return 1;
- }
-
- case CONST:
- case LABEL_REF:
- case SYMBOL_REF:
- return 3;
-
- case CONST_DOUBLE:
- return 20;
-
- default:
- return 4;
- }
-}
-
-static int
-h8300_and_costs (x)
- rtx x;
-{
- rtx operands[4];
+ rtx operands[4];
if (GET_MODE (x) == QImode)
return 1;
return 100;
operands[0] = NULL;
- operands[1] = NULL;
+ operands[1] = XEXP (x, 0);
operands[2] = XEXP (x, 1);
operands[3] = x;
return compute_logical_op_length (GET_MODE (x), operands) / 2;
}
+/* Compute the cost of a shift insn. */
+
static int
-h8300_shift_costs (x)
- rtx x;
+h8300_shift_costs (rtx x)
{
rtx operands[4];
return compute_a_shift_length (NULL, operands) / 2;
}
+/* Worker function for TARGET_RTX_COSTS. */
+
static bool
-h8300_rtx_costs (x, code, outer_code, total)
- rtx x;
- int code, outer_code;
- int *total;
+h8300_rtx_costs (rtx x, int code, int outer_code, int *total)
{
+ if (TARGET_H8300SX && outer_code == MEM)
+ {
+ /* Estimate the number of execution states needed to calculate
+ the address. */
+ if (register_operand (x, VOIDmode)
+ || GET_CODE (x) == POST_INC
+ || GET_CODE (x) == POST_DEC
+ || CONSTANT_P (x))
+ *total = 0;
+ else
+ *total = COSTS_N_INSNS (1);
+ return true;
+ }
+
switch (code)
{
+ case CONST_INT:
+ {
+ HOST_WIDE_INT n = INTVAL (x);
+
+ if (TARGET_H8300SX)
+ {
+ /* Constant operands need the same number of processor
+ states as register operands. Although we could try to
+ use a size-based cost for optimize_size, the lack of
+ of a mode makes the results very unpredictable. */
+ *total = 0;
+ return true;
+ }
+ if (-4 <= n || n <= 4)
+ {
+ switch ((int) n)
+ {
+ case 0:
+ *total = 0;
+ return true;
+ case 1:
+ case 2:
+ case -1:
+ case -2:
+ *total = 0 + (outer_code == SET);
+ return true;
+ case 4:
+ case -4:
+ if (TARGET_H8300H || TARGET_H8300S)
+ *total = 0 + (outer_code == SET);
+ else
+ *total = 1;
+ return true;
+ }
+ }
+ *total = 1;
+ return true;
+ }
+
+ case CONST:
+ case LABEL_REF:
+ case SYMBOL_REF:
+ if (TARGET_H8300SX)
+ {
+ /* See comment for CONST_INT. */
+ *total = 0;
+ return true;
+ }
+ *total = 3;
+ return true;
+
+ case CONST_DOUBLE:
+ *total = 20;
+ return true;
+
case AND:
+ if (!h8300_dst_operand (XEXP (x, 0), VOIDmode)
+ || !h8300_src_operand (XEXP (x, 1), VOIDmode))
+ return false;
*total = COSTS_N_INSNS (h8300_and_costs (x));
return true;
generate some really horrible code for division of a power of two. */
case MOD:
case DIV:
- *total = 60;
+ case UMOD:
+ case UDIV:
+ if (TARGET_H8300SX)
+ switch (GET_MODE (x))
+ {
+ case QImode:
+ case HImode:
+ *total = COSTS_N_INSNS (optimize_size ? 4 : 10);
+ return false;
+
+ case SImode:
+ *total = COSTS_N_INSNS (optimize_size ? 4 : 18);
+ return false;
+
+ default:
+ break;
+ }
+ *total = COSTS_N_INSNS (12);
return true;
case MULT:
- *total = 20;
+ if (TARGET_H8300SX)
+ switch (GET_MODE (x))
+ {
+ case QImode:
+ case HImode:
+ *total = COSTS_N_INSNS (2);
+ return false;
+
+ case SImode:
+ *total = COSTS_N_INSNS (5);
+ return false;
+
+ default:
+ break;
+ }
+ *total = COSTS_N_INSNS (4);
return true;
case ASHIFT:
case ASHIFTRT:
case LSHIFTRT:
+ if (h8sx_binary_shift_operator (x, VOIDmode))
+ {
+ *total = COSTS_N_INSNS (2);
+ return false;
+ }
+ else if (h8sx_unary_shift_operator (x, VOIDmode))
+ {
+ *total = COSTS_N_INSNS (1);
+ return false;
+ }
*total = COSTS_N_INSNS (h8300_shift_costs (x));
return true;
return true;
default:
- *total = const_costs (x, code, outer_code);
- return true;
+ *total = COSTS_N_INSNS (1);
+ return false;
}
}
\f
then +2. if const then least sig word
'j' print operand as condition code.
'k' print operand as reverse condition code.
+ 'm' convert an integer operand to a size suffix (.b, .w or .l)
+ 'o' print an integer without a leading '#'
's' print as low byte of 16 bit value
't' print as high byte of 16 bit value
'w' print as low byte of 32 bit value
/* Return assembly language string which identifies a comparison type. */
static const char *
-cond_string (code)
- enum rtx_code code;
+cond_string (enum rtx_code code)
{
switch (code)
{
FILE. */
void
-print_operand (file, x, code)
- FILE *file;
- rtx x;
- int code;
+print_operand (FILE *file, rtx x, int code)
{
/* This is used for communication between codes V,W,Z and Y. */
static int bitint;
case 'k':
fputs (cond_string (reverse_condition (GET_CODE (x))), file);
break;
+ case 'm':
+ if (GET_CODE (x) != CONST_INT)
+ abort ();
+ if (INTVAL (x) == 1)
+ fputs (".b", file);
+ else if (INTVAL (x) == 2)
+ fputs (".w", file);
+ else if (INTVAL (x) == 4)
+ fputs (".l", file);
+ else
+ abort ();
+ break;
+ case 'o':
+ print_operand_address (file, x);
+ break;
case 's':
if (GET_CODE (x) == CONST_INT)
fprintf (file, "#%ld", (INTVAL (x)) & 0xff);
fprintf (file, "@");
output_address (addr);
- /* 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;
- }
+ /* Add a length suffix to constant addresses. Although this
+ is often unnecessary, it helps to avoid ambiguity in the
+ syntax of mova. If we wrote an insn like:
+
+ mova/w.l @(1,@foo.b),er0
+
+ then .b would be considered part of the symbol name.
+ Adding a length after foo will avoid this. */
+ if (CONSTANT_P (addr))
+ 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 'X':
+ case 'T':
+ case 'S':
+ if (h8300_constant_length (addr) == 2)
+ fprintf (file, ":16");
+ else
+ fprintf (file, ":32");
+ break;
+ default:
+ break;
+ }
}
break;
/* Output assembly language output for the address ADDR to FILE. */
void
-print_operand_address (file, addr)
- FILE *file;
- rtx addr;
+print_operand_address (FILE *file, rtx addr)
{
+ rtx index;
+ int size;
+
switch (GET_CODE (addr))
{
case REG:
fprintf (file, "%s+", h8_reg_names[REGNO (XEXP (addr, 0))]);
break;
+ case PRE_INC:
+ fprintf (file, "+%s", h8_reg_names[REGNO (XEXP (addr, 0))]);
+ break;
+
+ case POST_DEC:
+ fprintf (file, "%s-", h8_reg_names[REGNO (XEXP (addr, 0))]);
+ break;
+
case PLUS:
fprintf (file, "(");
- if (GET_CODE (XEXP (addr, 0)) == REG)
+
+ index = h8300_get_index (XEXP (addr, 0), VOIDmode, &size);
+ if (GET_CODE (index) == REG)
{
/* reg,foo */
print_operand_address (file, XEXP (addr, 1));
fprintf (file, ",");
- print_operand_address (file, XEXP (addr, 0));
+ switch (size)
+ {
+ case 0:
+ print_operand_address (file, index);
+ break;
+
+ case 1:
+ print_operand (file, index, 'X');
+ fputs (".b", file);
+ break;
+
+ case 2:
+ print_operand (file, index, 'T');
+ fputs (".w", file);
+ break;
+
+ case 4:
+ print_operand (file, index, 'S');
+ fputs (".l", file);
+ break;
+ }
+ /* print_operand_address (file, XEXP (addr, 0)); */
}
else
{
option. */
void
-final_prescan_insn (insn, operand, num_operands)
- rtx insn, *operand ATTRIBUTE_UNUSED;
- int num_operands ATTRIBUTE_UNUSED;
+final_prescan_insn (rtx insn, rtx *operand ATTRIBUTE_UNUSED,
+ int num_operands ATTRIBUTE_UNUSED)
{
/* This holds the last insn address. */
static int last_insn_address = 0;
const 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),
/* Prepare for an SI sized move. */
int
-do_movsi (operands)
- rtx operands[];
+h8300_expand_movsi (rtx operands[])
{
rtx src = operands[1];
rtx dst = operands[0];
the other its replacement, at the start of a routine. */
int
-h8300_initial_elimination_offset (from, to)
- int from, to;
+h8300_initial_elimination_offset (int from, int to)
{
- int offset = 0;
/* The number of bytes that the return address takes on the stack. */
int pc_size = POINTER_SIZE / BITS_PER_UNIT;
- if (from == ARG_POINTER_REGNUM && to == FRAME_POINTER_REGNUM)
- offset = pc_size + 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;
+ /* The number of bytes that the saved frame pointer takes on the stack. */
+ int fp_size = frame_pointer_needed * UNITS_PER_WORD;
+
+ /* The number of bytes that the saved registers, excluding the frame
+ pointer, take on the stack. */
+ int saved_regs_size = 0;
- for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
- if (WORD_REG_USED (regno))
- offset += UNITS_PER_WORD;
+ /* The number of bytes that the locals takes on the stack. */
+ int frame_size = round_frame_size (get_frame_size ());
- /* See the comments for get_frame_size. We need to round it up to
- STACK_BOUNDARY. */
+ int regno;
- offset += round_frame_size (get_frame_size ());
+ for (regno = 0; regno <= HARD_FRAME_POINTER_REGNUM; regno++)
+ if (WORD_REG_USED (regno))
+ saved_regs_size += UNITS_PER_WORD;
- if (from == ARG_POINTER_REGNUM && to == STACK_POINTER_REGNUM)
- /* Skip saved PC. */
- offset += pc_size;
- }
+ /* Adjust saved_regs_size because the above loop took the frame
+ pointer int account. */
+ saved_regs_size -= fp_size;
- return offset;
+ if (to == HARD_FRAME_POINTER_REGNUM)
+ {
+ switch (from)
+ {
+ case ARG_POINTER_REGNUM:
+ return pc_size + fp_size;
+ case RETURN_ADDRESS_POINTER_REGNUM:
+ return fp_size;
+ case FRAME_POINTER_REGNUM:
+ return -saved_regs_size;
+ default:
+ abort ();
+ }
+ }
+ else if (to == STACK_POINTER_REGNUM)
+ {
+ switch (from)
+ {
+ case ARG_POINTER_REGNUM:
+ return pc_size + saved_regs_size + frame_size;
+ case RETURN_ADDRESS_POINTER_REGNUM:
+ return saved_regs_size + frame_size;
+ case FRAME_POINTER_REGNUM:
+ return frame_size;
+ default:
+ abort ();
+ }
+ }
+ else
+ abort ();
}
+/* Worker function for RETURN_ADDR_RTX. */
+
rtx
-h8300_return_addr_rtx (count, frame)
- int count;
- rtx frame;
+h8300_return_addr_rtx (int count, rtx frame)
{
rtx ret;
/* Update the condition code from the insn. */
void
-notice_update_cc (body, insn)
- rtx body;
- rtx insn;
+notice_update_cc (rtx body, rtx insn)
{
rtx set;
break;
}
}
+\f
+/* Given that X occurs in an address of the form (plus X constant),
+ return the part of X that is expected to be a register. There are
+ four kinds of addressing mode to recognize:
-/* Return nonzero if X is a stack pointer. */
+ @(dd,Rn)
+ @(dd,RnL.b)
+ @(dd,Rn.w)
+ @(dd,ERn.l)
-int
-stack_pointer_operand (x, mode)
- rtx x;
- enum machine_mode mode ATTRIBUTE_UNUSED;
-{
- return x == stack_pointer_rtx;
-}
+ If SIZE is nonnull, and the address is one of the last three forms,
+ set *SIZE to the index multiplication factor. Set it to 0 for
+ plain @(dd,Rn) addresses.
-/* Return nonzero if X is a constant whose absolute value is greater
- than 2. */
+ MODE is the mode of the value being accessed. It can be VOIDmode
+ if the address is known to be valid, but its mode is unknown. */
-int
-const_int_gt_2_operand (x, mode)
- rtx x;
- enum machine_mode mode ATTRIBUTE_UNUSED;
+rtx
+h8300_get_index (rtx x, enum machine_mode mode, int *size)
{
- return (GET_CODE (x) == CONST_INT
- && abs (INTVAL (x)) > 2);
-}
+ int dummy, factor;
-/* Return nonzero if X is a constant whose absolute value is no
- smaller than 8. */
+ if (size == 0)
+ size = &dummy;
-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);
+ factor = (mode == VOIDmode ? 0 : GET_MODE_SIZE (mode));
+ if (TARGET_H8300SX
+ && factor <= 4
+ && (mode == VOIDmode
+ || GET_MODE_CLASS (mode) == MODE_INT
+ || GET_MODE_CLASS (mode) == MODE_FLOAT))
+ {
+ if (factor <= 1 && GET_CODE (x) == ZERO_EXTEND)
+ {
+ /* When accessing byte-sized values, the index can be
+ a zero-extended QImode or HImode register. */
+ *size = GET_MODE_SIZE (GET_MODE (XEXP (x, 0)));
+ return XEXP (x, 0);
+ }
+ else
+ {
+ /* We're looking for addresses of the form:
+
+ (mult X I)
+ or (mult (zero_extend X) I)
+
+ where I is the size of the operand being accessed.
+ The canonical form of the second expression is:
+
+ (and (mult (subreg X) I) J)
+
+ where J == GET_MODE_MASK (GET_MODE (X)) * I. */
+ rtx index;
+
+ if (GET_CODE (x) == AND
+ && GET_CODE (XEXP (x, 1)) == CONST_INT
+ && (factor == 0
+ || INTVAL (XEXP (x, 1)) == 0xff * factor
+ || INTVAL (XEXP (x, 1)) == 0xffff * factor))
+ {
+ index = XEXP (x, 0);
+ *size = (INTVAL (XEXP (x, 1)) >= 0xffff ? 2 : 1);
+ }
+ else
+ {
+ index = x;
+ *size = 4;
+ }
+
+ if (GET_CODE (index) == MULT
+ && GET_CODE (XEXP (index, 1)) == CONST_INT
+ && (factor == 0 || factor == INTVAL (XEXP (index, 1))))
+ return XEXP (index, 0);
+ }
+ }
+ *size = 0;
+ return x;
}
+\f
+static const h8300_length_table addb_length_table =
+{
+ /* #xx Rs @aa @Rs @xx */
+ { 2, 2, 4, 4, 4 }, /* add.b xx,Rd */
+ { 4, 4, 4, 4, 6 }, /* add.b xx,@aa */
+ { 4, 4, 4, 4, 6 }, /* add.b xx,@Rd */
+ { 6, 4, 4, 4, 6 } /* add.b xx,@xx */
+};
-/* Return nonzero if X is a constant expressible in QImode. */
+static const h8300_length_table addw_length_table =
+{
+ /* #xx Rs @aa @Rs @xx */
+ { 2, 2, 4, 4, 4 }, /* add.w xx,Rd */
+ { 4, 4, 4, 4, 6 }, /* add.w xx,@aa */
+ { 4, 4, 4, 4, 6 }, /* add.w xx,@Rd */
+ { 4, 4, 4, 4, 6 } /* add.w xx,@xx */
+};
-int
-const_int_qi_operand (x, mode)
- rtx x;
- enum machine_mode mode ATTRIBUTE_UNUSED;
+static const h8300_length_table addl_length_table =
{
- return (GET_CODE (x) == CONST_INT
- && (INTVAL (x) & 0xff) == INTVAL (x));
-}
+ /* #xx Rs @aa @Rs @xx */
+ { 2, 2, 4, 4, 4 }, /* add.l xx,Rd */
+ { 4, 4, 6, 6, 6 }, /* add.l xx,@aa */
+ { 4, 4, 6, 6, 6 }, /* add.l xx,@Rd */
+ { 4, 4, 6, 6, 6 } /* add.l xx,@xx */
+};
-/* Return nonzero if X is a constant expressible in HImode. */
+#define logicb_length_table addb_length_table
+#define logicw_length_table addw_length_table
-int
-const_int_hi_operand (x, mode)
- rtx x;
- enum machine_mode mode ATTRIBUTE_UNUSED;
+static const h8300_length_table logicl_length_table =
{
- return (GET_CODE (x) == CONST_INT
- && (INTVAL (x) & 0xffff) == INTVAL (x));
-}
+ /* #xx Rs @aa @Rs @xx */
+ { 2, 4, 4, 4, 4 }, /* and.l xx,Rd */
+ { 4, 4, 6, 6, 6 }, /* and.l xx,@aa */
+ { 4, 4, 6, 6, 6 }, /* and.l xx,@Rd */
+ { 4, 4, 6, 6, 6 } /* and.l xx,@xx */
+};
+
+static const h8300_length_table movb_length_table =
+{
+ /* #xx Rs @aa @Rs @xx */
+ { 2, 2, 2, 2, 4 }, /* mov.b xx,Rd */
+ { 4, 2, 4, 4, 4 }, /* mov.b xx,@aa */
+ { 4, 2, 4, 4, 4 }, /* mov.b xx,@Rd */
+ { 4, 4, 4, 4, 4 } /* mov.b xx,@xx */
+};
-/* Return nonzero if X is a constant suitable for inc/dec. */
+#define movw_length_table movb_length_table
-int
-incdec_operand (x, mode)
- rtx x;
- enum machine_mode mode ATTRIBUTE_UNUSED;
+static const h8300_length_table movl_length_table =
{
- return (GET_CODE (x) == CONST_INT
- && (CONST_OK_FOR_M (INTVAL (x))
- || CONST_OK_FOR_O (INTVAL (x))));
-}
+ /* #xx Rs @aa @Rs @xx */
+ { 2, 2, 4, 4, 4 }, /* mov.l xx,Rd */
+ { 4, 4, 4, 4, 4 }, /* mov.l xx,@aa */
+ { 4, 4, 4, 4, 4 }, /* mov.l xx,@Rd */
+ { 4, 4, 4, 4, 4 } /* mov.l xx,@xx */
+};
-/* Return nonzero if X is either EQ or NE. */
+/* Return the size of the given address or displacement constant. */
-int
-eqne_operator (x, mode)
- rtx x;
- enum machine_mode mode ATTRIBUTE_UNUSED;
+static unsigned int
+h8300_constant_length (rtx constant)
{
- enum rtx_code code = GET_CODE (x);
+ /* Check for (@d:16,Reg). */
+ if (GET_CODE (constant) == CONST_INT
+ && IN_RANGE (INTVAL (constant), -0x8000, 0x7fff))
+ return 2;
+
+ /* Check for (@d:16,Reg) in cases where the displacement is
+ an absolute address. */
+ if (Pmode == HImode || h8300_tiny_constant_address_p (constant))
+ return 2;
- return (code == EQ || code == NE);
+ return 4;
}
-/* Return nonzero if X is GT, LE, GTU, or LEU. */
+/* Return the size of a displacement field in address ADDR, which should
+ have the form (plus X constant). SIZE is the number of bytes being
+ accessed. */
-int
-gtle_operator (x, mode)
- rtx x;
- enum machine_mode mode ATTRIBUTE_UNUSED;
+static unsigned int
+h8300_displacement_length (rtx addr, int size)
{
- enum rtx_code code = GET_CODE (x);
+ rtx offset;
+
+ offset = XEXP (addr, 1);
+
+ /* Check for @(d:2,Reg). */
+ if (register_operand (XEXP (addr, 0), VOIDmode)
+ && GET_CODE (offset) == CONST_INT
+ && (INTVAL (offset) == size
+ || INTVAL (offset) == size * 2
+ || INTVAL (offset) == size * 3))
+ return 0;
- return (code == GT || code == LE || code == GTU || code == LEU);
+ return h8300_constant_length (offset);
}
-/* Return nonzero if X is either GTU or LEU. */
+/* Store the class of operand OP in *CLASS and return the length of any
+ extra operand fields. SIZE is the number of bytes in OP. CLASS
+ can be null if only the length is needed. */
-int
-gtuleu_operator (x, mode)
- rtx x;
- enum machine_mode mode ATTRIBUTE_UNUSED;
+static unsigned int
+h8300_classify_operand (rtx op, int size, enum h8300_operand_class *class)
{
- enum rtx_code code = GET_CODE (x);
+ enum h8300_operand_class dummy;
- return (code == GTU || code == LEU);
-}
+ if (class == 0)
+ class = &dummy;
-/* Return nonzero if X is either IOR or XOR. */
+ if (CONSTANT_P (op))
+ {
+ *class = H8OP_IMMEDIATE;
-int
-iorxor_operator (x, mode)
- rtx x;
- enum machine_mode mode ATTRIBUTE_UNUSED;
-{
- enum rtx_code code = GET_CODE (x);
+ /* Byte-sized immediates are stored in the opcode fields. */
+ if (size == 1)
+ return 0;
- return (code == IOR || code == XOR);
+ /* If this is a 32-bit instruction, see whether the constant
+ will fit into a 16-bit immediate field. */
+ if (TARGET_H8300SX
+ && size == 4
+ && GET_CODE (op) == CONST_INT
+ && IN_RANGE (INTVAL (op), 0, 0xffff))
+ return 2;
+
+ return size;
+ }
+ else if (GET_CODE (op) == MEM)
+ {
+ op = XEXP (op, 0);
+ if (CONSTANT_P (op))
+ {
+ *class = H8OP_MEM_ABSOLUTE;
+ return h8300_constant_length (op);
+ }
+ else if (GET_CODE (op) == PLUS && CONSTANT_P (XEXP (op, 1)))
+ {
+ *class = H8OP_MEM_COMPLEX;
+ return h8300_displacement_length (op, size);
+ }
+ else if (GET_RTX_CLASS (GET_CODE (op)) == RTX_AUTOINC)
+ {
+ *class = H8OP_MEM_COMPLEX;
+ return 0;
+ }
+ else if (register_operand (op, VOIDmode))
+ {
+ *class = H8OP_MEM_BASE;
+ return 0;
+ }
+ }
+ else if (register_operand (op, VOIDmode))
+ {
+ *class = H8OP_REGISTER;
+ return 0;
+ }
+ abort ();
}
-/* Recognize valid operators for bit instructions. */
+/* Return the length of the instruction described by TABLE given that
+ its operands are OP1 and OP2. OP1 must be an h8300_dst_operand
+ and OP2 must be an h8300_src_operand. */
-int
-bit_operator (x, mode)
- rtx x;
- enum machine_mode mode ATTRIBUTE_UNUSED;
+static unsigned int
+h8300_length_from_table (rtx op1, rtx op2, const h8300_length_table *table)
{
- enum rtx_code code = GET_CODE (x);
+ enum h8300_operand_class op1_class, op2_class;
+ unsigned int size, immediate_length;
- return (code == XOR
- || code == AND
- || code == IOR);
+ size = GET_MODE_SIZE (GET_MODE (op1));
+ immediate_length = (h8300_classify_operand (op1, size, &op1_class)
+ + h8300_classify_operand (op2, size, &op2_class));
+ return immediate_length + (*table)[op1_class - 1][op2_class];
}
-\f
-const char *
-output_plussi (operands)
- rtx *operands;
-{
- enum machine_mode mode = GET_MODE (operands[0]);
- if (mode != SImode)
- abort ();
+/* Return the length of a unary instruction such as neg or not given that
+ its operand is OP. */
- if (TARGET_H8300)
+unsigned int
+h8300_unary_length (rtx op)
+{
+ enum h8300_operand_class class;
+ unsigned int size, operand_length;
+
+ size = GET_MODE_SIZE (GET_MODE (op));
+ operand_length = h8300_classify_operand (op, size, &class);
+ switch (class)
{
- if (GET_CODE (operands[2]) == REG)
- return "add.w\t%f2,%f0\n\taddx\t%y2,%y0\n\taddx\t%z2,%z0";
+ case H8OP_REGISTER:
+ return 2;
- if (GET_CODE (operands[2]) == CONST_INT)
- {
- HOST_WIDE_INT n = INTVAL (operands[2]);
+ case H8OP_MEM_BASE:
+ return (size == 4 ? 6 : 4);
- if ((n & 0xffffff) == 0)
- return "add\t%z2,%z0";
- if ((n & 0xffff) == 0)
- return "add\t%y2,%y0\n\taddx\t%z2,%z0";
- if ((n & 0xff) == 0)
- return "add\t%x2,%x0\n\taddx\t%y2,%y0\n\taddx\t%z2,%z0";
- }
+ case H8OP_MEM_ABSOLUTE:
+ return operand_length + (size == 4 ? 6 : 4);
- return "add\t%w2,%w0\n\taddx\t%x2,%x0\n\taddx\t%y2,%y0\n\taddx\t%z2,%z0";
+ case H8OP_MEM_COMPLEX:
+ return operand_length + 6;
+
+ default:
+ abort ();
}
- else
- {
- if (GET_CODE (operands[2]) == REG)
- return "add.l\t%S2,%S0";
+}
- if (GET_CODE (operands[2]) == CONST_INT)
- {
- HOST_WIDE_INT intval = INTVAL (operands[2]);
+/* Likewise short immediate instructions such as add.w #xx:3,OP. */
- /* See if we can finish with 2 bytes. */
+static unsigned int
+h8300_short_immediate_length (rtx op)
+{
+ enum h8300_operand_class class;
+ unsigned int size, operand_length;
- switch ((unsigned int) intval & 0xffffffff)
- {
+ size = GET_MODE_SIZE (GET_MODE (op));
+ operand_length = h8300_classify_operand (op, size, &class);
+
+ switch (class)
+ {
+ case H8OP_REGISTER:
+ return 2;
+
+ case H8OP_MEM_BASE:
+ case H8OP_MEM_ABSOLUTE:
+ case H8OP_MEM_COMPLEX:
+ return 4 + operand_length;
+
+ default:
+ abort ();
+ }
+}
+
+/* Likewise bitfield load and store instructions. */
+
+static unsigned int
+h8300_bitfield_length (rtx op, rtx op2)
+{
+ enum h8300_operand_class class;
+ unsigned int size, operand_length;
+
+ if (GET_CODE (op) == REG)
+ op = op2;
+ if (GET_CODE (op) == REG)
+ abort ();
+
+ size = GET_MODE_SIZE (GET_MODE (op));
+ operand_length = h8300_classify_operand (op, size, &class);
+
+ switch (class)
+ {
+ case H8OP_MEM_BASE:
+ case H8OP_MEM_ABSOLUTE:
+ case H8OP_MEM_COMPLEX:
+ return 4 + operand_length;
+
+ default:
+ abort ();
+ }
+}
+
+/* Calculate the length of general binary instruction INSN using TABLE. */
+
+static unsigned int
+h8300_binary_length (rtx insn, const h8300_length_table *table)
+{
+ rtx set;
+
+ set = single_set (insn);
+ if (set == 0)
+ abort ();
+
+ if (BINARY_P (SET_SRC (set)))
+ return h8300_length_from_table (XEXP (SET_SRC (set), 0),
+ XEXP (SET_SRC (set), 1), table);
+ else if (GET_RTX_CLASS (GET_CODE (SET_SRC (set))) == RTX_TERNARY)
+ return h8300_length_from_table (XEXP (XEXP (SET_SRC (set), 1), 0),
+ XEXP (XEXP (SET_SRC (set), 1), 1),
+ table);
+ else
+ abort ();
+}
+
+/* Subroutine of h8300_move_length. Return true if OP is 1- or 2-byte
+ memory reference and either (1) it has the form @(d:16,Rn) or
+ (2) its address has the code given by INC_CODE. */
+
+static bool
+h8300_short_move_mem_p (rtx op, enum rtx_code inc_code)
+{
+ rtx addr;
+ unsigned int size;
+
+ if (GET_CODE (op) != MEM)
+ return false;
+
+ addr = XEXP (op, 0);
+ size = GET_MODE_SIZE (GET_MODE (op));
+ if (size != 1 && size != 2)
+ return false;
+
+ return (GET_CODE (addr) == inc_code
+ || (GET_CODE (addr) == PLUS
+ && GET_CODE (XEXP (addr, 0)) == REG
+ && h8300_displacement_length (addr, size) == 2));
+}
+
+/* Calculate the length of move instruction INSN using the given length
+ table. Although the tables are correct for most cases, there is some
+ irregularity in the length of mov.b and mov.w. The following forms:
+
+ mov @ERs+, Rd
+ mov @(d:16,ERs), Rd
+ mov Rs, @-ERd
+ mov Rs, @(d:16,ERd)
+
+ are two bytes shorter than most other "mov Rs, @complex" or
+ "mov @complex,Rd" combinations. */
+
+static unsigned int
+h8300_move_length (rtx *operands, const h8300_length_table *table)
+{
+ unsigned int size;
+
+ size = h8300_length_from_table (operands[0], operands[1], table);
+ if (REG_P (operands[0]) && h8300_short_move_mem_p (operands[1], POST_INC))
+ size -= 2;
+ if (REG_P (operands[1]) && h8300_short_move_mem_p (operands[0], PRE_DEC))
+ size -= 2;
+ return size;
+}
+
+/* Return the length of a mova instruction with the given operands.
+ DEST is the register destination, SRC is the source address and
+ OFFSET is the 16-bit or 32-bit displacement. */
+
+static unsigned int
+h8300_mova_length (rtx dest, rtx src, rtx offset)
+{
+ unsigned int size;
+
+ size = (2
+ + h8300_constant_length (offset)
+ + h8300_classify_operand (src, GET_MODE_SIZE (GET_MODE (src)), 0));
+ if (!REG_P (dest) || !REG_P (src) || REGNO (src) != REGNO (dest))
+ size += 2;
+ return size;
+}
+
+/* Compute the length of INSN based on its length_table attribute.
+ OPERANDS is the array of its operands. */
+
+unsigned int
+h8300_insn_length_from_table (rtx insn, rtx * operands)
+{
+ switch (get_attr_length_table (insn))
+ {
+ case LENGTH_TABLE_NONE:
+ abort ();
+
+ case LENGTH_TABLE_ADDB:
+ return h8300_binary_length (insn, &addb_length_table);
+
+ case LENGTH_TABLE_ADDW:
+ return h8300_binary_length (insn, &addw_length_table);
+
+ case LENGTH_TABLE_ADDL:
+ return h8300_binary_length (insn, &addl_length_table);
+
+ case LENGTH_TABLE_LOGICB:
+ return h8300_binary_length (insn, &logicb_length_table);
+
+ case LENGTH_TABLE_MOVB:
+ return h8300_move_length (operands, &movb_length_table);
+
+ case LENGTH_TABLE_MOVW:
+ return h8300_move_length (operands, &movw_length_table);
+
+ case LENGTH_TABLE_MOVL:
+ return h8300_move_length (operands, &movl_length_table);
+
+ case LENGTH_TABLE_MOVA:
+ return h8300_mova_length (operands[0], operands[1], operands[2]);
+
+ case LENGTH_TABLE_MOVA_ZERO:
+ return h8300_mova_length (operands[0], operands[1], const0_rtx);
+
+ case LENGTH_TABLE_UNARY:
+ return h8300_unary_length (operands[0]);
+
+ case LENGTH_TABLE_MOV_IMM4:
+ return 2 + h8300_classify_operand (operands[0], 0, 0);
+
+ case LENGTH_TABLE_SHORT_IMMEDIATE:
+ return h8300_short_immediate_length (operands[0]);
+
+ case LENGTH_TABLE_BITFIELD:
+ return h8300_bitfield_length (operands[0], operands[1]);
+
+ case LENGTH_TABLE_BITBRANCH:
+ return h8300_bitfield_length (operands[1], operands[2]) - 2;
+
+ }
+ abort ();
+}
+
+/* Return true if LHS and RHS are memory references that can be mapped
+ to the same h8sx assembly operand. LHS appears as the destination of
+ an instruction and RHS appears as a source.
+
+ Three cases are allowed:
+
+ - RHS is @+Rn or @-Rn, LHS is @Rn
+ - RHS is @Rn, LHS is @Rn+ or @Rn-
+ - RHS and LHS have the same address and neither has side effects. */
+
+bool
+h8sx_mergeable_memrefs_p (rtx lhs, rtx rhs)
+{
+ if (GET_CODE (rhs) == MEM && GET_CODE (lhs) == MEM)
+ {
+ rhs = XEXP (rhs, 0);
+ lhs = XEXP (lhs, 0);
+
+ if (GET_CODE (rhs) == PRE_INC || GET_CODE (rhs) == PRE_DEC)
+ return rtx_equal_p (XEXP (rhs, 0), lhs);
+
+ if (GET_CODE (lhs) == POST_INC || GET_CODE (lhs) == POST_DEC)
+ return rtx_equal_p (rhs, XEXP (lhs, 0));
+
+ if (rtx_equal_p (rhs, lhs))
+ return true;
+ }
+ return false;
+}
+
+/* Return true if OPERANDS[1] can be mapped to the same assembly
+ operand as OPERANDS[0]. */
+
+bool
+h8300_operands_match_p (rtx *operands)
+{
+ if (register_operand (operands[0], VOIDmode)
+ && register_operand (operands[1], VOIDmode))
+ return true;
+
+ if (h8sx_mergeable_memrefs_p (operands[0], operands[1]))
+ return true;
+
+ return false;
+}
+\f
+/* Try using movmd to move LENGTH bytes from memory region SRC to memory
+ region DEST. The two regions do not overlap and have the common
+ alignment given by ALIGNMENT. Return true on success.
+
+ Using movmd for variable-length moves seems to involve some
+ complex trade-offs. For instance:
+
+ - Preparing for a movmd instruction is similar to preparing
+ for a memcpy. The main difference is that the arguments
+ are moved into er4, er5 and er6 rather than er0, er1 and er2.
+
+ - Since movmd clobbers the frame pointer, we need to save
+ and restore it somehow when frame_pointer_needed. This can
+ sometimes make movmd sequences longer than calls to memcpy().
+
+ - The counter register is 16 bits, so the instruction is only
+ suitable for variable-length moves when sizeof (size_t) == 2.
+ That's only true in normal mode.
+
+ - We will often lack static alignment information. Falling back
+ on movmd.b would likely be slower than calling memcpy(), at least
+ for big moves.
+
+ This function therefore only uses movmd when the length is a
+ known constant, and only then if -fomit-frame-pointer is in
+ effect or if we're not optimizing for size.
+
+ At the moment the function uses movmd for all in-range constants,
+ but it might be better to fall back on memcpy() for large moves
+ if ALIGNMENT == 1. */
+
+bool
+h8sx_emit_movmd (rtx dest, rtx src, rtx length,
+ HOST_WIDE_INT alignment)
+{
+ if (!flag_omit_frame_pointer && optimize_size)
+ return false;
+
+ if (GET_CODE (length) == CONST_INT)
+ {
+ rtx dest_reg, src_reg, first_dest, first_src;
+ HOST_WIDE_INT n;
+ int factor;
+
+ /* Use movmd.l if the alignment allows it, otherwise fall back
+ on movmd.b. */
+ factor = (alignment >= 2 ? 4 : 1);
+
+ /* Make sure the length is within range. We can handle counter
+ values up to 65536, although HImode truncation will make
+ the count appear negative in rtl dumps. */
+ n = INTVAL (length);
+ if (n <= 0 || n / factor > 65536)
+ return false;
+
+ /* Create temporary registers for the source and destination
+ pointers. Initialize them to the start of each region. */
+ dest_reg = copy_addr_to_reg (XEXP (dest, 0));
+ src_reg = copy_addr_to_reg (XEXP (src, 0));
+
+ /* Create references to the movmd source and destination blocks. */
+ first_dest = replace_equiv_address (dest, dest_reg);
+ first_src = replace_equiv_address (src, src_reg);
+
+ set_mem_size (first_dest, GEN_INT (n & -factor));
+ set_mem_size (first_src, GEN_INT (n & -factor));
+
+ length = copy_to_mode_reg (HImode, gen_int_mode (n / factor, HImode));
+ emit_insn (gen_movmd (first_dest, first_src, length, GEN_INT (factor)));
+
+ if ((n & -factor) != n)
+ {
+ /* Move SRC and DEST past the region we just copied.
+ This is done to update the memory attributes. */
+ dest = adjust_address (dest, BLKmode, n & -factor);
+ src = adjust_address (src, BLKmode, n & -factor);
+
+ /* Replace the addresses with the source and destination
+ registers, which movmd has left with the right values. */
+ dest = replace_equiv_address (dest, dest_reg);
+ src = replace_equiv_address (src, src_reg);
+
+ /* Mop up the left-over bytes. */
+ if (n & 2)
+ emit_move_insn (adjust_address (dest, HImode, 0),
+ adjust_address (src, HImode, 0));
+ if (n & 1)
+ emit_move_insn (adjust_address (dest, QImode, n & 2),
+ adjust_address (src, QImode, n & 2));
+ }
+ return true;
+ }
+ return false;
+}
+
+/* Move ADDR into er6 after pushing its old value onto the stack. */
+
+void
+h8300_swap_into_er6 (rtx addr)
+{
+ push (HARD_FRAME_POINTER_REGNUM);
+ emit_move_insn (hard_frame_pointer_rtx, addr);
+ if (REGNO (addr) == SP_REG)
+ emit_move_insn (hard_frame_pointer_rtx,
+ plus_constant (hard_frame_pointer_rtx,
+ GET_MODE_SIZE (word_mode)));
+}
+
+/* Move the current value of er6 into ADDR and pop its old value
+ from the stack. */
+
+void
+h8300_swap_out_of_er6 (rtx addr)
+{
+ if (REGNO (addr) != SP_REG)
+ emit_move_insn (addr, hard_frame_pointer_rtx);
+ pop (HARD_FRAME_POINTER_REGNUM);
+}
+\f
+/* Return the length of mov instruction. */
+
+unsigned int
+compute_mov_length (rtx *operands)
+{
+ /* If the mov instruction involves a memory operand, we compute the
+ length, assuming the largest addressing mode is used, and then
+ adjust later in the function. Otherwise, we compute and return
+ the exact length in one step. */
+ enum machine_mode mode = GET_MODE (operands[0]);
+ rtx dest = operands[0];
+ rtx src = operands[1];
+ rtx addr;
+
+ if (GET_CODE (src) == MEM)
+ addr = XEXP (src, 0);
+ else if (GET_CODE (dest) == MEM)
+ addr = XEXP (dest, 0);
+ else
+ addr = NULL_RTX;
+
+ if (TARGET_H8300)
+ {
+ unsigned int base_length;
+
+ switch (mode)
+ {
+ case QImode:
+ if (addr == NULL_RTX)
+ return 2;
+
+ /* The eightbit addressing is available only in QImode, so
+ go ahead and take care of it. */
+ if (h8300_eightbit_constant_address_p (addr))
+ return 2;
+
+ base_length = 4;
+ break;
+
+ case HImode:
+ if (addr == NULL_RTX)
+ {
+ if (REG_P (src))
+ return 2;
+
+ if (src == const0_rtx)
+ return 2;
+
+ return 4;
+ }
+
+ base_length = 4;
+ break;
+
+ case SImode:
+ if (addr == NULL_RTX)
+ {
+ if (REG_P (src))
+ return 4;
+
+ if (GET_CODE (src) == CONST_INT)
+ {
+ if (src == const0_rtx)
+ return 4;
+
+ if ((INTVAL (src) & 0xffff) == 0)
+ return 6;
+
+ if ((INTVAL (src) & 0xffff) == 0)
+ return 6;
+
+ if ((INTVAL (src) & 0xffff)
+ == ((INTVAL (src) >> 16) & 0xffff))
+ return 6;
+ }
+ return 8;
+ }
+
+ base_length = 8;
+ break;
+
+ case SFmode:
+ if (addr == NULL_RTX)
+ {
+ if (REG_P (src))
+ return 4;
+
+ if (CONST_DOUBLE_OK_FOR_LETTER_P (src, 'G'))
+ return 4;
+
+ return 8;
+ }
+
+ base_length = 8;
+ break;
+
+ default:
+ abort ();
+ }
+
+ /* Adjust the length based on the addressing mode used.
+ Specifically, we subtract the difference between the actual
+ length and the longest one, which is @(d:16,Rs). For SImode
+ and SFmode, we double the adjustment because two mov.w are
+ used to do the job. */
+
+ /* @Rs+ and @-Rd are 2 bytes shorter than the longest. */
+ if (GET_CODE (addr) == PRE_DEC
+ || GET_CODE (addr) == POST_INC)
+ {
+ if (mode == QImode || mode == HImode)
+ return base_length - 2;
+ else
+ /* In SImode and SFmode, we use two mov.w instructions, so
+ double the adjustment. */
+ return base_length - 4;
+ }
+
+ /* @Rs and @Rd are 2 bytes shorter than the longest. Note that
+ in SImode and SFmode, the second mov.w involves an address
+ with displacement, namely @(2,Rs) or @(2,Rd), so we subtract
+ only 2 bytes. */
+ if (GET_CODE (addr) == REG)
+ return base_length - 2;
+
+ return base_length;
+ }
+ else
+ {
+ unsigned int base_length;
+
+ switch (mode)
+ {
+ case QImode:
+ if (addr == NULL_RTX)
+ return 2;
+
+ /* The eightbit addressing is available only in QImode, so
+ go ahead and take care of it. */
+ if (h8300_eightbit_constant_address_p (addr))
+ return 2;
+
+ base_length = 8;
+ break;
+
+ case HImode:
+ if (addr == NULL_RTX)
+ {
+ if (REG_P (src))
+ return 2;
+
+ if (src == const0_rtx)
+ return 2;
+
+ return 4;
+ }
+
+ base_length = 8;
+ break;
+
+ case SImode:
+ if (addr == NULL_RTX)
+ {
+ if (REG_P (src))
+ {
+ if (REGNO (src) == MAC_REG || REGNO (dest) == MAC_REG)
+ return 4;
+ else
+ return 2;
+ }
+
+ if (GET_CODE (src) == CONST_INT)
+ {
+ int val = INTVAL (src);
+
+ if (val == 0)
+ return 2;
+
+ if (val == (val & 0x00ff) || val == (val & 0xff00))
+ return 4;
+
+ switch (val & 0xffffffff)
+ {
+ case 0xffffffff:
+ case 0xfffffffe:
+ case 0xfffffffc:
+ case 0x0000ffff:
+ case 0x0000fffe:
+ case 0xffff0000:
+ case 0xfffe0000:
+ case 0x00010000:
+ case 0x00020000:
+ return 4;
+ }
+ }
+ return 6;
+ }
+
+ base_length = 10;
+ break;
+
+ case SFmode:
+ if (addr == NULL_RTX)
+ {
+ if (REG_P (src))
+ return 2;
+
+ if (CONST_DOUBLE_OK_FOR_LETTER_P (src, 'G'))
+ return 2;
+
+ return 6;
+ }
+
+ base_length = 10;
+ break;
+
+ default:
+ abort ();
+ }
+
+ /* Adjust the length based on the addressing mode used.
+ Specifically, we subtract the difference between the actual
+ length and the longest one, which is @(d:24,ERs). */
+
+ /* @ERs+ and @-ERd are 6 bytes shorter than the longest. */
+ if (GET_CODE (addr) == PRE_DEC
+ || GET_CODE (addr) == POST_INC)
+ return base_length - 6;
+
+ /* @ERs and @ERd are 6 bytes shorter than the longest. */
+ if (GET_CODE (addr) == REG)
+ return base_length - 6;
+
+ /* @(d:16,ERs) and @(d:16,ERd) are 4 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 base_length - 4;
+
+ /* @aa:16 is 4 bytes shorter than the longest. */
+ if (h8300_tiny_constant_address_p (addr))
+ return base_length - 4;
+
+ /* @aa:24 is 2 bytes shorter than the longest. */
+ if (CONSTANT_P (addr))
+ return base_length - 2;
+
+ return base_length;
+ }
+}
+\f
+/* Output an addition insn. */
+
+const char *
+output_plussi (rtx *operands)
+{
+ enum machine_mode mode = GET_MODE (operands[0]);
+
+ if (mode != SImode)
+ abort ();
+
+ if (TARGET_H8300)
+ {
+ if (GET_CODE (operands[2]) == REG)
+ return "add.w\t%f2,%f0\n\taddx\t%y2,%y0\n\taddx\t%z2,%z0";
+
+ if (GET_CODE (operands[2]) == CONST_INT)
+ {
+ HOST_WIDE_INT n = INTVAL (operands[2]);
+
+ if ((n & 0xffffff) == 0)
+ return "add\t%z2,%z0";
+ if ((n & 0xffff) == 0)
+ return "add\t%y2,%y0\n\taddx\t%z2,%z0";
+ if ((n & 0xff) == 0)
+ return "add\t%x2,%x0\n\taddx\t%y2,%y0\n\taddx\t%z2,%z0";
+ }
+
+ return "add\t%w2,%w0\n\taddx\t%x2,%x0\n\taddx\t%y2,%y0\n\taddx\t%z2,%z0";
+ }
+ else
+ {
+ if (GET_CODE (operands[2]) == CONST_INT
+ && register_operand (operands[1], VOIDmode))
+ {
+ HOST_WIDE_INT intval = INTVAL (operands[2]);
+
+ if (TARGET_H8300SX && (intval >= 1 && intval <= 7))
+ return "add.l\t%S2,%S0";
+ if (TARGET_H8300SX && (intval >= -7 && intval <= -1))
+ return "sub.l\t%G2,%S0";
+
+ /* See if we can finish with 2 bytes. */
+
+ switch ((unsigned int) intval & 0xffffffff)
+ {
case 0x00000001:
case 0x00000002:
case 0x00000004:
}
}
+ if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) < 0)
+ {
+ operands[2] = GEN_INT (-INTVAL (operands[2]));
+ return "sub.l\t%S2,%S0";
+ }
return "add.l\t%S2,%S0";
}
}
+/* ??? It would be much easier to add the h8sx stuff if a single function
+ classified the addition as either inc/dec, adds/subs, add.w or add.l. */
+/* Compute the length of an addition insn. */
+
unsigned int
-compute_plussi_length (operands)
- rtx *operands;
+compute_plussi_length (rtx *operands)
{
enum machine_mode mode = GET_MODE (operands[0]);
}
else
{
- if (GET_CODE (operands[2]) == REG)
- return 2;
-
- if (GET_CODE (operands[2]) == CONST_INT)
+ if (GET_CODE (operands[2]) == CONST_INT
+ && register_operand (operands[1], VOIDmode))
{
HOST_WIDE_INT intval = INTVAL (operands[2]);
+ if (TARGET_H8300SX && (intval >= 1 && intval <= 7))
+ return 2;
+ if (TARGET_H8300SX && (intval >= -7 && intval <= -1))
+ return 2;
+
/* See if we can finish with 2 bytes. */
switch ((unsigned int) intval & 0xffffffff)
return 4;
}
+ if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) < 0)
+ return h8300_length_from_table (operands[0],
+ GEN_INT (-INTVAL (operands[2])),
+ &addl_length_table);
+ else
+ return h8300_length_from_table (operands[0], operands[2],
+ &addl_length_table);
return 6;
}
}
+/* Compute which flag bits are valid after an addition insn. */
+
int
-compute_plussi_cc (operands)
- rtx *operands;
+compute_plussi_cc (rtx *operands)
{
enum machine_mode mode = GET_MODE (operands[0]);
}
else
{
- if (GET_CODE (operands[2]) == REG)
- return CC_SET_ZN;
-
- if (GET_CODE (operands[2]) == CONST_INT)
+ if (GET_CODE (operands[2]) == CONST_INT
+ && register_operand (operands[1], VOIDmode))
{
HOST_WIDE_INT intval = INTVAL (operands[2]);
+ if (TARGET_H8300SX && (intval >= 1 && intval <= 7))
+ return CC_SET_ZN;
+ if (TARGET_H8300SX && (intval >= -7 && intval <= -1))
+ return CC_SET_ZN;
+
/* See if we can finish with 2 bytes. */
switch ((unsigned int) intval & 0xffffffff)
}
}
\f
+/* Output a logical insn. */
+
const char *
-output_logical_op (mode, operands)
- enum machine_mode mode;
- rtx *operands;
+output_logical_op (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. */
const unsigned HOST_WIDE_INT intval =
(unsigned HOST_WIDE_INT) ((GET_CODE (operands[2]) == CONST_INT)
+ /* Always use the full instruction if the
+ first operand is in memory. It is better
+ to use define_splits to generate the shorter
+ sequence where valid. */
+ && register_operand (operands[1], VOIDmode)
? INTVAL (operands[2]) : 0x55555555);
/* The determinant of the algorithm. If we perform an AND, 0
affects a bit. Otherwise, 1 affects a bit. */
return "";
}
+/* Compute the length of a logical insn. */
+
unsigned int
-compute_logical_op_length (mode, operands)
- enum machine_mode mode;
- rtx *operands;
+compute_logical_op_length (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. */
const unsigned HOST_WIDE_INT intval =
(unsigned HOST_WIDE_INT) ((GET_CODE (operands[2]) == CONST_INT)
+ /* Always use the full instruction if the
+ first operand is in memory. It is better
+ to use define_splits to generate the shorter
+ sequence where valid. */
+ && register_operand (operands[1], VOIDmode)
? INTVAL (operands[2]) : 0x55555555);
/* The determinant of the algorithm. If we perform an AND, 0
affects a bit. Otherwise, 1 affects a bit. */
&& b0 != 0
&& b1 != 0)
{
- if (REG_P (operands[2]))
- length += 2;
- else
- length += 4;
+ length = h8300_length_from_table (operands[1], operands[2],
+ &logicw_length_table);
}
else
{
&& !(code == IOR && w1 == 0xffff
&& (w0 & 0x8000) != 0 && lower_half_easy_p))
{
- if (REG_P (operands[2]))
- length += 4;
- else
- length += 6;
+ length = h8300_length_from_table (operands[1], operands[2],
+ &logicl_length_table);
}
else
{
return length;
}
+/* Compute which flag bits are valid after a logical insn. */
+
int
-compute_logical_op_cc (mode, operands)
- enum machine_mode mode;
- rtx *operands;
+compute_logical_op_cc (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. */
const unsigned HOST_WIDE_INT intval =
(unsigned HOST_WIDE_INT) ((GET_CODE (operands[2]) == CONST_INT)
+ /* Always use the full instruction if the
+ first operand is in memory. It is better
+ to use define_splits to generate the shorter
+ sequence where valid. */
+ && register_operand (operands[1], VOIDmode)
? INTVAL (operands[2]) : 0x55555555);
/* The determinant of the algorithm. If we perform an AND, 0
affects a bit. Otherwise, 1 affects a bit. */
return cc;
}
\f
+/* Expand a conditional branch. */
+
+void
+h8300_expand_branch (enum rtx_code code, rtx label)
+{
+ rtx tmp;
+
+ tmp = gen_rtx_fmt_ee (code, VOIDmode, cc0_rtx, const0_rtx);
+ tmp = gen_rtx_IF_THEN_ELSE (VOIDmode, tmp,
+ gen_rtx_LABEL_REF (VOIDmode, label),
+ pc_rtx);
+ emit_jump_insn (gen_rtx_SET (VOIDmode, pc_rtx, tmp));
+}
+\f
/* Shifts.
We devote a fair bit of code to getting efficient shifts since we
For the details of the shift algorithms for various shift counts,
refer to shift_alg_[qhs]i. */
-int
-nshift_operator (x, mode)
- rtx x;
- enum machine_mode mode ATTRIBUTE_UNUSED;
+/* Classify a shift with the given mode and code. OP is the shift amount. */
+
+enum h8sx_shift_type
+h8sx_classify_shift (enum machine_mode mode, enum rtx_code code, rtx op)
{
- switch (GET_CODE (x))
+ if (!TARGET_H8300SX)
+ return H8SX_SHIFT_NONE;
+
+ switch (code)
{
- case ASHIFTRT:
+ case ASHIFT:
case LSHIFTRT:
+ /* Check for variable shifts (shll Rs,Rd and shlr Rs,Rd). */
+ if (GET_CODE (op) != CONST_INT)
+ return H8SX_SHIFT_BINARY;
+
+ /* Reject out-of-range shift amounts. */
+ if (INTVAL (op) <= 0 || INTVAL (op) >= GET_MODE_BITSIZE (mode))
+ return H8SX_SHIFT_NONE;
+
+ /* Power-of-2 shifts are effectively unary operations. */
+ if (exact_log2 (INTVAL (op)) >= 0)
+ return H8SX_SHIFT_UNARY;
+
+ return H8SX_SHIFT_BINARY;
+
+ case ASHIFTRT:
+ if (op == const1_rtx || op == const2_rtx)
+ return H8SX_SHIFT_UNARY;
+ return H8SX_SHIFT_NONE;
+
+ case ROTATE:
+ if (GET_CODE (op) == CONST_INT
+ && (INTVAL (op) == 1
+ || INTVAL (op) == 2
+ || INTVAL (op) == GET_MODE_BITSIZE (mode) - 2
+ || INTVAL (op) == GET_MODE_BITSIZE (mode) - 1))
+ return H8SX_SHIFT_UNARY;
+ return H8SX_SHIFT_NONE;
+
+ default:
+ return H8SX_SHIFT_NONE;
+ }
+}
+
+/* Return the asm template for a single h8sx shift instruction.
+ OPERANDS[0] and OPERANDS[1] are the destination, OPERANDS[2]
+ is the source and OPERANDS[3] is the shift. SUFFIX is the
+ size suffix ('b', 'w' or 'l') and OPTYPE is the print_operand
+ prefix for the destination operand. */
+
+const char *
+output_h8sx_shift (rtx *operands, int suffix, int optype)
+{
+ static char buffer[16];
+ const char *stem;
+
+ switch (GET_CODE (operands[3]))
+ {
case ASHIFT:
- return 1;
+ stem = "shll";
+ break;
+
+ case ASHIFTRT:
+ stem = "shar";
+ break;
+
+ case LSHIFTRT:
+ stem = "shlr";
+ break;
+
+ case ROTATE:
+ stem = "rotl";
+ if (INTVAL (operands[2]) > 2)
+ {
+ /* This is really a right rotate. */
+ operands[2] = GEN_INT (GET_MODE_BITSIZE (GET_MODE (operands[0]))
+ - INTVAL (operands[2]));
+ stem = "rotr";
+ }
+ break;
default:
- return 0;
+ abort ();
}
+ if (operands[2] == const1_rtx)
+ sprintf (buffer, "%s.%c\t%%%c0", stem, suffix, optype);
+ else
+ sprintf (buffer, "%s.%c\t%%X2,%%%c0", stem, suffix, optype);
+ return buffer;
}
/* Emit code to do shifts. */
-void
-expand_a_shift (mode, code, operands)
- enum machine_mode mode;
- int code;
- rtx operands[];
+bool
+expand_a_shift (enum machine_mode mode, int code, rtx operands[])
{
+ switch (h8sx_classify_shift (mode, code, operands[2]))
+ {
+ case H8SX_SHIFT_BINARY:
+ operands[1] = force_reg (mode, operands[1]);
+ return false;
+
+ case H8SX_SHIFT_UNARY:
+ return false;
+
+ case H8SX_SHIFT_NONE:
+ break;
+ }
+
emit_move_insn (operands[0], operands[1]);
/* Need a loop to get all the bits we want - we generate the
(VOIDmode,
gen_rtvec (2,
gen_rtx_SET (VOIDmode, operands[0],
- gen_rtx (code, mode, operands[0],
- operands[2])),
+ gen_rtx_fmt_ee (code, mode,
+ operands[0], operands[2])),
gen_rtx_CLOBBER (VOIDmode,
gen_rtx_SCRATCH (QImode)))));
+ return true;
}
/* Symbols of the various modes which can be used as indices. */
int cc_special;
};
-static void get_shift_alg PARAMS ((enum shift_type,
- enum shift_mode, unsigned int,
- struct shift_info *));
+static void get_shift_alg (enum shift_type,
+ enum shift_mode, unsigned int,
+ struct shift_info *);
/* Given SHIFT_TYPE, SHIFT_MODE, and shift count COUNT, determine the
best algorithm for doing the shift. The assembler code is stored
result is returned through 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;
+get_shift_alg (enum shift_type shift_type, enum shift_mode shift_mode,
+ unsigned int count, struct shift_info *info)
{
enum h8_cpu cpu;
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;
+h8300_shift_needs_scratch_p (int count, enum machine_mode mode)
{
enum h8_cpu cpu;
int a, lr, ar;
|| (TARGET_H8300H && mode == SImode && count == 8));
}
-/* Emit the assembler code for doing shifts. */
+/* Output the assembler code for doing shifts. */
const char *
-output_a_shift (operands)
- rtx *operands;
+output_a_shift (rtx *operands)
{
static int loopend_lab;
rtx shift = operands[3];
}
}
+/* Count the number of assembly instructions in a string TEMPLATE. */
+
static unsigned int
-h8300_asm_insn_count (template)
- const char *template;
+h8300_asm_insn_count (const char *template)
{
unsigned int count = 1;
return count;
}
+/* Compute the length of a shift insn. */
+
unsigned int
-compute_a_shift_length (insn, operands)
- rtx insn ATTRIBUTE_UNUSED;
- rtx *operands;
+compute_a_shift_length (rtx insn ATTRIBUTE_UNUSED, rtx *operands)
{
rtx shift = operands[3];
enum machine_mode mode = GET_MODE (shift);
}
}
+/* Compute which flag bits are valid after a shift insn. */
+
int
-compute_a_shift_cc (insn, operands)
- rtx insn ATTRIBUTE_UNUSED;
- rtx *operands;
+compute_a_shift_cc (rtx insn ATTRIBUTE_UNUSED, rtx *operands)
{
rtx shift = operands[3];
enum machine_mode mode = GET_MODE (shift);
/* 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,
including the one in the loop, will be taken care of by
- emit_a_rotate () at the insn emit time. */
+ output_a_rotate () at the insn emit time. */
int
-expand_a_rotate (code, operands)
- enum rtx_code code;
- rtx operands[];
+expand_a_rotate (rtx operands[])
{
rtx dst = operands[0];
rtx src = operands[1];
rtx rotate_amount = operands[2];
enum machine_mode mode = GET_MODE (dst);
- rtx tmp;
+
+ if (h8sx_classify_shift (mode, ROTATE, rotate_amount) == H8SX_SHIFT_UNARY)
+ return false;
/* We rotate in place. */
emit_move_insn (dst, src);
/* If the rotate amount is less than or equal to 0,
we go out of the loop. */
- emit_cmp_and_jump_insns (rotate_amount, GEN_INT (0), LE, NULL_RTX,
+ emit_cmp_and_jump_insns (rotate_amount, const0_rtx, LE, NULL_RTX,
QImode, 0, end_label);
/* Initialize the loop counter. */
emit_label (start_label);
/* Rotate by one bit. */
- tmp = gen_rtx (code, mode, dst, GEN_INT (1));
- emit_insn (gen_rtx_SET (mode, dst, tmp));
+ switch (mode)
+ {
+ case QImode:
+ emit_insn (gen_rotlqi3_1 (dst, dst, const1_rtx));
+ break;
+ case HImode:
+ emit_insn (gen_rotlhi3_1 (dst, dst, const1_rtx));
+ break;
+ case SImode:
+ emit_insn (gen_rotlsi3_1 (dst, dst, const1_rtx));
+ break;
+ default:
+ abort ();
+ }
/* Decrement the counter by 1. */
- tmp = gen_rtx_PLUS (QImode, counter, GEN_INT (-1));
- emit_insn (gen_rtx_SET (VOIDmode, counter, tmp));
+ emit_insn (gen_addqi3 (counter, counter, constm1_rtx));
/* 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,
+ emit_cmp_and_jump_insns (counter, const0_rtx, NE, NULL_RTX, QImode, 1,
start_label);
emit_label (end_label);
else
{
/* Rotate by AMOUNT bits. */
- tmp = gen_rtx (code, mode, dst, rotate_amount);
- emit_insn (gen_rtx_SET (mode, dst, tmp));
+ switch (mode)
+ {
+ case QImode:
+ emit_insn (gen_rotlqi3_1 (dst, dst, rotate_amount));
+ break;
+ case HImode:
+ emit_insn (gen_rotlhi3_1 (dst, dst, rotate_amount));
+ break;
+ case SImode:
+ emit_insn (gen_rotlsi3_1 (dst, dst, rotate_amount));
+ break;
+ default:
+ abort ();
+ }
}
return 1;
}
-/* Emit rotate insns. */
+/* Output a rotate insn. */
const char *
-emit_a_rotate (code, operands)
- enum rtx_code code;
- rtx *operands;
+output_a_rotate (enum rtx_code code, rtx *operands)
{
rtx dst = operands[0];
rtx rotate_amount = operands[2];
(rotate_type == SHIFT_ASHIFT) ? SHIFT_LSHIFTRT : SHIFT_ASHIFT;
}
- /* Emit rotate insns. */
+ /* Output rotate insns. */
for (bits = TARGET_H8300S ? 2 : 1; bits > 0; bits /= 2)
{
if (bits == 2)
return "";
}
+
+/* Compute the length of a rotate insn. */
+
+unsigned int
+compute_a_rotate_length (rtx *operands)
+{
+ rtx src = operands[1];
+ rtx amount_rtx = operands[2];
+ enum machine_mode mode = GET_MODE (src);
+ int amount;
+ unsigned int length = 0;
+
+ if (GET_CODE (amount_rtx) != CONST_INT)
+ abort ();
+
+ amount = INTVAL (amount_rtx);
+
+ /* Clean up AMOUNT. */
+ if (amount < 0)
+ amount = 0;
+ if ((unsigned int) amount > GET_MODE_BITSIZE (mode))
+ amount = GET_MODE_BITSIZE (mode);
+
+ /* 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) / (unsigned) 2)
+ /* Flip the direction. */
+ amount = GET_MODE_BITSIZE (mode) - amount;
+
+ /* See if a byte swap (in HImode) or a word swap (in SImode) can
+ boost up the rotation. */
+ 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))
+ {
+ /* Adjust AMOUNT and flip the direction. */
+ amount = GET_MODE_BITSIZE (mode) / 2 - amount;
+ length += 6;
+ }
+
+ /* We use 2-bit rotations on the H8S. */
+ if (TARGET_H8300S)
+ amount = amount / 2 + amount % 2;
+
+ /* The H8/300 uses three insns to rotate one bit, taking 6
+ length. */
+ length += amount * ((TARGET_H8300 && mode == HImode) ? 6 : 2);
+
+ return length;
+}
\f
/* Fix the operands of a gen_xxx so that it could become a bit
operating insn. */
int
-fix_bit_operand (operands, what, type)
- rtx *operands;
- int what;
- enum rtx_code type;
+fix_bit_operand (rtx *operands, enum rtx_code code)
{
/* The bit_operand predicate accepts any memory during RTL generation, but
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 ((what == 0 && single_zero_operand (operands[2], QImode))
- || (what == 1 && single_one_operand (operands[2], QImode)))
+ if (code == AND
+ ? single_zero_operand (operands[2], QImode)
+ : single_one_operand (operands[2], QImode))
{
/* OK to have a memory dest. */
if (GET_CODE (operands[0]) == MEM
- && !EXTRA_CONSTRAINT (operands[0], 'U'))
+ && !OK_FOR_U (operands[0]))
{
rtx mem = gen_rtx_MEM (GET_MODE (operands[0]),
copy_to_mode_reg (Pmode,
}
if (GET_CODE (operands[1]) == MEM
- && !EXTRA_CONSTRAINT (operands[1], 'U'))
+ && !OK_FOR_U (operands[1]))
{
rtx mem = gen_rtx_MEM (GET_MODE (operands[1]),
copy_to_mode_reg (Pmode,
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));
+ switch (code)
+ {
+ case AND:
+ emit_insn (gen_andqi3_1 (res, operands[1], operands[2]));
+ break;
+ case IOR:
+ emit_insn (gen_iorqi3_1 (res, operands[1], operands[2]));
+ break;
+ case XOR:
+ emit_insn (gen_xorqi3_1 (res, operands[1], operands[2]));
+ break;
+ default:
+ abort ();
+ }
+ emit_insn (gen_movqi (operands[0], res));
}
return 1;
}
by the "interrupt" attribute. */
static int
-h8300_interrupt_function_p (func)
- tree func;
+h8300_interrupt_function_p (tree func)
{
tree a;
return a != NULL_TREE;
}
+/* Return nonzero if FUNC is a saveall function as specified by the
+ "saveall" attribute. */
+
+static int
+h8300_saveall_function_p (tree func)
+{
+ tree a;
+
+ if (TREE_CODE (func) != FUNCTION_DECL)
+ return 0;
+
+ a = lookup_attribute ("saveall", DECL_ATTRIBUTES (func));
+ return a != NULL_TREE;
+}
+
/* Return nonzero if FUNC is an OS_Task function as specified
by the "OS_Task" attribute. */
static int
-h8300_os_task_function_p (func)
- tree func;
+h8300_os_task_function_p (tree func)
{
tree a;
by the "monitor" attribute. */
static int
-h8300_monitor_function_p (func)
- tree func;
+h8300_monitor_function_p (tree func)
{
tree a;
through the function vector. */
int
-h8300_funcvec_function_p (func)
- tree func;
+h8300_funcvec_function_p (tree func)
{
tree a;
data area. */
int
-h8300_eightbit_data_p (decl)
- tree decl;
+h8300_eightbit_data_p (tree decl)
{
tree a;
data area. */
int
-h8300_tiny_data_p (decl)
- tree decl;
+h8300_tiny_data_p (tree decl)
{
tree a;
return a != NULL_TREE;
}
-/* Generate an 'interrupt_handler' attribute for decls. */
+/* Generate an 'interrupt_handler' attribute for decls. We convert
+ all the pragmas to corresponding attributes. */
static void
-h8300_insert_attributes (node, attributes)
- tree node;
- tree *attributes;
+h8300_insert_attributes (tree node, tree *attributes)
{
- if (!pragma_interrupt
- || TREE_CODE (node) != FUNCTION_DECL)
- return;
+ if (TREE_CODE (node) == FUNCTION_DECL)
+ {
+ if (pragma_interrupt)
+ {
+ pragma_interrupt = 0;
+
+ /* Add an 'interrupt_handler' attribute. */
+ *attributes = tree_cons (get_identifier ("interrupt_handler"),
+ NULL, *attributes);
+ }
- pragma_interrupt = 0;
+ if (pragma_saveall)
+ {
+ pragma_saveall = 0;
- /* Add an 'interrupt_handler' attribute. */
- *attributes = tree_cons (get_identifier ("interrupt_handler"),
- NULL, *attributes);
+ /* Add an 'saveall' attribute. */
+ *attributes = tree_cons (get_identifier ("saveall"),
+ NULL, *attributes);
+ }
+ }
}
/* Supported attributes:
interrupt_handler: output a prologue and epilogue suitable for an
interrupt handler.
+ saveall: output a prologue and epilogue that saves and restores
+ all registers except the stack pointer.
+
function_vector: This function should be called through the
function vector.
{
/* { name, min_len, max_len, decl_req, type_req, fn_type_req, handler } */
{ "interrupt_handler", 0, 0, true, false, false, h8300_handle_fndecl_attribute },
+ { "saveall", 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 },
/* Handle an attribute requiring a FUNCTION_DECL; arguments as in
struct attribute_spec.handler. */
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;
+h8300_handle_fndecl_attribute (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",
+ warning (0, "%qs attribute only applies to functions",
IDENTIFIER_POINTER (name));
*no_add_attrs = true;
}
/* Handle an "eightbit_data" attribute; arguments as in
struct attribute_spec.handler. */
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;
+h8300_handle_eightbit_data_attribute (tree *node, tree name,
+ tree args ATTRIBUTE_UNUSED,
+ int flags ATTRIBUTE_UNUSED,
+ bool *no_add_attrs)
{
tree decl = *node;
}
else
{
- warning ("`%s' attribute ignored", IDENTIFIER_POINTER (name));
+ warning (0, "%qs attribute ignored", IDENTIFIER_POINTER (name));
*no_add_attrs = true;
}
/* Handle an "tiny_data" attribute; arguments as in
struct attribute_spec.handler. */
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;
+h8300_handle_tiny_data_attribute (tree *node, tree name,
+ tree args ATTRIBUTE_UNUSED,
+ int flags ATTRIBUTE_UNUSED,
+ bool *no_add_attrs)
{
tree decl = *node;
}
else
{
- warning ("`%s' attribute ignored", IDENTIFIER_POINTER (name));
+ warning (0, "%qs attribute ignored", IDENTIFIER_POINTER (name));
*no_add_attrs = true;
}
/* Mark function vectors, and various small data objects. */
static void
-h8300_encode_section_info (decl, rtl, first)
- tree decl;
- rtx rtl;
- int first;
+h8300_encode_section_info (tree decl, rtx rtl, int first)
{
int extra_flags = 0;
SYMBOL_REF_FLAGS (XEXP (rtl, 0)) |= extra_flags;
}
+/* Output a single-bit extraction. */
+
const char *
-output_simode_bld (bild, operands)
- int bild;
- rtx operands[];
+output_simode_bld (int bild, rtx operands[])
{
if (TARGET_H8300)
{
output_asm_insn ("xor.l\t%S0,%S0", operands);
/* Perform the bit store. */
- output_asm_insn ("bst\t#0,%w0", operands);
+ output_asm_insn ("rotxl.l\t%S0", operands);
}
/* All done. */
return "";
}
-/* Given INSN and its current length LENGTH, return the adjustment
- (in bytes) to correctly compute INSN's length.
-
- We use this to get the lengths of various memory references correct. */
+/* Delayed-branch scheduling is more effective if we have some idea
+ how long each instruction will be. Use a shorten_branches pass
+ to get an initial estimate. */
-int
-h8300_adjust_insn_length (insn, length)
- rtx insn;
- int length ATTRIBUTE_UNUSED;
+static void
+h8300_reorg (void)
{
- rtx pat = PATTERN (insn);
-
- /* We must filter these out before calling get_attr_adjust_length. */
- 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;
-
- /* Adjust length for reg->mem and mem->reg copies. */
- if (GET_CODE (pat) == SET
- && (GET_CODE (SET_SRC (pat)) == MEM
- || GET_CODE (SET_DEST (pat)) == MEM))
- {
- /* This insn might need a length adjustment. */
- 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)
- {
- /* On the H8/300, we subtract the difference between the
- actual length and the longest one, which is @(d:16,ERs). */
-
- /* @Rs is 2 bytes shorter than the longest. */
- if (GET_CODE (addr) == REG)
- 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. */
- 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;
- }
- }
- }
-
- /* Rotations need various adjustments. */
- 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));
-
- /* Clean up AMOUNT. */
- if (amount < 0)
- amount = 0;
- if ((unsigned int) amount > GET_MODE_BITSIZE (mode))
- amount = GET_MODE_BITSIZE (mode);
-
- /* 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) / (unsigned) 2)
- /* Flip the direction. */
- amount = GET_MODE_BITSIZE (mode) - amount;
-
- /* See if a byte swap (in HImode) or a word swap (in SImode) can
- boost up the rotation. */
- 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))
- {
- /* Adjust AMOUNT and flip the direction. */
- amount = GET_MODE_BITSIZE (mode) / 2 - amount;
- states += 6;
- }
-
- /* We use 2-bit rotations on the H8S. */
- if (TARGET_H8300S)
- amount = amount / 2 + amount % 2;
-
- /* The H8/300 uses three insns to rotate one bit, taking 6
- states. */
- states += amount * ((TARGET_H8300 && mode == HImode) ? 6 : 2);
-
- return -(20 - states);
- }
-
- return 0;
+ if (flag_delayed_branch)
+ shorten_branches (get_insns ());
}
#ifndef OBJECT_FORMAT_ELF
static void
-h8300_asm_named_section (name, flags)
- const char *name;
- unsigned int flags ATTRIBUTE_UNUSED;
+h8300_asm_named_section (const char *name, unsigned int flags ATTRIBUTE_UNUSED,
+ tree decl)
{
/* ??? Perhaps we should be using default_coff_asm_named_section. */
fprintf (asm_out_file, "\t.section %s\n", name);
which is a special case of the 'R' operand. */
int
-h8300_eightbit_constant_address_p (x)
- rtx x;
+h8300_eightbit_constant_address_p (rtx x)
{
- /* The ranges of the 8-bit area. */
+ /* 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);
on H8/300H and H8S. */
int
-h8300_tiny_constant_address_p (x)
- rtx x;
+h8300_tiny_constant_address_p (rtx x)
{
/* The ranges of the 16-bit area. */
const unsigned HOST_WIDE_INT h1 = trunc_int_for_mode (0x00000000, SImode);
unsigned HOST_WIDE_INT addr;
- /* We accept symbols declared with tiny_data. */
- if (GET_CODE (x) == SYMBOL_REF)
- return (SYMBOL_REF_FLAGS (x) & SYMBOL_FLAG_TINY_DATA) != 0;
+ switch (GET_CODE (x))
+ {
+ case SYMBOL_REF:
+ /* In the normal mode, any symbol fits in the 16-bit absolute
+ address range. We also accept symbols declared with
+ tiny_data. */
+ return (TARGET_NORMAL_MODE
+ || (SYMBOL_REF_FLAGS (x) & SYMBOL_FLAG_TINY_DATA) != 0);
- if (GET_CODE (x) != CONST_INT)
- return 0;
+ case CONST_INT:
+ addr = INTVAL (x);
+ return (TARGET_NORMAL_MODE
+ || (TARGET_H8300H
+ && (IN_RANGE (addr, h1, h2) || IN_RANGE (addr, h3, h4)))
+ || (TARGET_H8300S
+ && (IN_RANGE (addr, s1, s2) || IN_RANGE (addr, s3, s4))));
- addr = INTVAL (x);
+ case CONST:
+ return TARGET_NORMAL_MODE;
+
+ default:
+ return 0;
+ }
- return (0
- || TARGET_NORMAL_MODE
- || (TARGET_H8300H
- && (IN_RANGE (addr, h1, h2) || IN_RANGE (addr, h3, h4)))
- || (TARGET_H8300S
- && (IN_RANGE (addr, s1, s2) || IN_RANGE (addr, s3, s4))));
}
+/* Return nonzero if ADDR1 and ADDR2 point to consecutive memory
+ locations that can be accessed as a 16-bit word. */
+
int
-byte_accesses_mergeable_p (addr1, addr2)
- rtx addr1, addr2;
+byte_accesses_mergeable_p (rtx addr1, rtx addr2)
{
HOST_WIDE_INT offset1, offset2;
rtx reg1, reg2;
return 0;
}
+
+/* Return nonzero if we have the same comparison insn as I3 two insns
+ before I3. I3 is assumed to be a comparison insn. */
+
+int
+same_cmp_preceding_p (rtx i3)
+{
+ rtx i1, i2;
+
+ /* Make sure we have a sequence of three insns. */
+ i2 = prev_nonnote_insn (i3);
+ if (i2 == NULL_RTX)
+ return 0;
+ i1 = prev_nonnote_insn (i2);
+ if (i1 == NULL_RTX)
+ return 0;
+
+ return (INSN_P (i1) && rtx_equal_p (PATTERN (i1), PATTERN (i3))
+ && any_condjump_p (i2) && onlyjump_p (i2));
+}
+
+/* Return nonzero if we have the same comparison insn as I1 two insns
+ after I1. I1 is assumed to be a comparison insn. */
+
+int
+same_cmp_following_p (rtx i1)
+{
+ rtx i2, i3;
+
+ /* Make sure we have a sequence of three insns. */
+ i2 = next_nonnote_insn (i1);
+ if (i2 == NULL_RTX)
+ return 0;
+ i3 = next_nonnote_insn (i2);
+ if (i3 == NULL_RTX)
+ return 0;
+
+ return (INSN_P (i3) && rtx_equal_p (PATTERN (i1), PATTERN (i3))
+ && any_condjump_p (i2) && onlyjump_p (i2));
+}
+
+/* Return nonzero if OPERANDS are valid for stm (or ldm) that pushes
+ (or pops) N registers. OPERANDS are assumed to be an array of
+ registers. */
+
+int
+h8300_regs_ok_for_stm (int n, rtx operands[])
+{
+ switch (n)
+ {
+ case 2:
+ return ((REGNO (operands[0]) == 0 && REGNO (operands[1]) == 1)
+ || (REGNO (operands[0]) == 2 && REGNO (operands[1]) == 3)
+ || (REGNO (operands[0]) == 4 && REGNO (operands[1]) == 5));
+ case 3:
+ return ((REGNO (operands[0]) == 0
+ && REGNO (operands[1]) == 1
+ && REGNO (operands[2]) == 2)
+ || (REGNO (operands[0]) == 4
+ && REGNO (operands[1]) == 5
+ && REGNO (operands[2]) == 6));
+
+ case 4:
+ return (REGNO (operands[0]) == 0
+ && REGNO (operands[1]) == 1
+ && REGNO (operands[2]) == 2
+ && REGNO (operands[3]) == 3);
+ }
+
+ abort ();
+}
+
+/* Return nonzero if register OLD_REG can be renamed to register NEW_REG. */
+
+int
+h8300_hard_regno_rename_ok (unsigned int old_reg ATTRIBUTE_UNUSED,
+ unsigned int new_reg)
+{
+ /* Interrupt functions can only use registers that have already been
+ saved by the prologue, even if they would normally be
+ call-clobbered. */
+
+ if (h8300_current_function_interrupt_function_p ()
+ && !regs_ever_live[new_reg])
+ return 0;
+
+ return 1;
+}
+
+/* Return nonzero if X is a legitimate constant. */
+
+int
+h8300_legitimate_constant_p (rtx x ATTRIBUTE_UNUSED)
+{
+ return 1;
+}
+
+/* Return nonzero if X is a REG or SUBREG suitable as a base register. */
+
+static int
+h8300_rtx_ok_for_base_p (rtx x, int strict)
+{
+ /* Strip off SUBREG if any. */
+ if (GET_CODE (x) == SUBREG)
+ x = SUBREG_REG (x);
+
+ return (REG_P (x)
+ && (strict
+ ? REG_OK_FOR_BASE_STRICT_P (x)
+ : REG_OK_FOR_BASE_NONSTRICT_P (x)));
+}
+
+/* Return nozero if X is a legitimate address. On the H8/300, a
+ legitimate address has the form REG, REG+CONSTANT_ADDRESS or
+ CONSTANT_ADDRESS. */
+
+int
+h8300_legitimate_address_p (enum machine_mode mode, rtx x, int strict)
+{
+ /* The register indirect addresses like @er0 is always valid. */
+ if (h8300_rtx_ok_for_base_p (x, strict))
+ return 1;
+
+ if (CONSTANT_ADDRESS_P (x))
+ return 1;
+
+ if (TARGET_H8300SX
+ && ( GET_CODE (x) == PRE_INC
+ || GET_CODE (x) == PRE_DEC
+ || GET_CODE (x) == POST_INC
+ || GET_CODE (x) == POST_DEC)
+ && h8300_rtx_ok_for_base_p (XEXP (x, 0), strict))
+ return 1;
+
+ if (GET_CODE (x) == PLUS
+ && CONSTANT_ADDRESS_P (XEXP (x, 1))
+ && h8300_rtx_ok_for_base_p (h8300_get_index (XEXP (x, 0),
+ mode, 0), strict))
+ return 1;
+
+ return 0;
+}
+
+/* Worker function for HARD_REGNO_NREGS.
+
+ We pretend the MAC register is 32bits -- we don't have any data
+ types on the H8 series to handle more than 32bits. */
+
+int
+h8300_hard_regno_nregs (int regno ATTRIBUTE_UNUSED, enum machine_mode mode)
+{
+ return (GET_MODE_SIZE (mode) + UNITS_PER_WORD - 1) / UNITS_PER_WORD;
+}
+
+/* Worker function for HARD_REGNO_MODE_OK. */
+
+int
+h8300_hard_regno_mode_ok (int regno, enum machine_mode mode)
+{
+ if (TARGET_H8300)
+ /* If an even reg, then anything goes. Otherwise the mode must be
+ QI or HI. */
+ return ((regno & 1) == 0) || (mode == HImode) || (mode == QImode);
+ else
+ /* MAC register can only be of SImode. Otherwise, anything
+ goes. */
+ return regno == MAC_REG ? mode == SImode : 1;
+}
+\f
+/* Perform target dependent optabs initialization. */
+static void
+h8300_init_libfuncs (void)
+{
+ set_optab_libfunc (smul_optab, HImode, "__mulhi3");
+ set_optab_libfunc (sdiv_optab, HImode, "__divhi3");
+ set_optab_libfunc (udiv_optab, HImode, "__udivhi3");
+ set_optab_libfunc (smod_optab, HImode, "__modhi3");
+ set_optab_libfunc (umod_optab, HImode, "__umodhi3");
+}
+\f
+/* Worker function for TARGET_RETURN_IN_MEMORY. */
+
+static bool
+h8300_return_in_memory (tree type, tree fntype ATTRIBUTE_UNUSED)
+{
+ return (TYPE_MODE (type) == BLKmode
+ || GET_MODE_SIZE (TYPE_MODE (type)) > (TARGET_H8300 ? 4 : 8));
+}
+\f
+/* Initialize the GCC target structure. */
+#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_FILE_START
+#define TARGET_ASM_FILE_START h8300_file_start
+#undef TARGET_ASM_FILE_START_FILE_DIRECTIVE
+#define TARGET_ASM_FILE_START_FILE_DIRECTIVE true
+
+#undef TARGET_ASM_FILE_END
+#define TARGET_ASM_FILE_END h8300_file_end
+
+#undef TARGET_ENCODE_SECTION_INFO
+#define TARGET_ENCODE_SECTION_INFO h8300_encode_section_info
+
+#undef TARGET_INSERT_ATTRIBUTES
+#define TARGET_INSERT_ATTRIBUTES h8300_insert_attributes
+
+#undef TARGET_RTX_COSTS
+#define TARGET_RTX_COSTS h8300_rtx_costs
+
+#undef TARGET_INIT_LIBFUNCS
+#define TARGET_INIT_LIBFUNCS h8300_init_libfuncs
+
+#undef TARGET_RETURN_IN_MEMORY
+#define TARGET_RETURN_IN_MEMORY h8300_return_in_memory
+
+#undef TARGET_MACHINE_DEPENDENT_REORG
+#define TARGET_MACHINE_DEPENDENT_REORG h8300_reorg
+
+struct gcc_target targetm = TARGET_INITIALIZER;