/* Subroutines for insn-output.c for Renesas H8/300.
- Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000,
- 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010
- Free Software Foundation, Inc.
+ Copyright (C) 1992-2020 Free Software Foundation, Inc.
Contributed by Steve Chamberlain (sac@cygnus.com),
Jim Wilson (wilson@cygnus.com), and Doug Evans (dje@cygnus.com).
along with GCC; see the file COPYING3. If not see
<http://www.gnu.org/licenses/>. */
+#define IN_TARGET_CODE 1
+
#include "config.h"
#include "system.h"
#include "coretypes.h"
-#include "tm.h"
+#include "backend.h"
+#include "target.h"
#include "rtl.h"
#include "tree.h"
+#include "df.h"
+#include "memmodel.h"
+#include "tm_p.h"
+#include "stringpool.h"
+#include "attribs.h"
+#include "optabs.h"
#include "regs.h"
-#include "hard-reg-set.h"
-#include "insn-config.h"
+#include "emit-rtl.h"
+#include "recog.h"
+#include "diagnostic-core.h"
+#include "alias.h"
+#include "stor-layout.h"
+#include "varasm.h"
+#include "calls.h"
#include "conditions.h"
#include "output.h"
#include "insn-attr.h"
#include "flags.h"
-#include "recog.h"
+#include "explow.h"
#include "expr.h"
-#include "function.h"
-#include "optabs.h"
-#include "diagnostic-core.h"
-#include "toplev.h"
-#include "c-family/c-pragma.h" /* ??? */
-#include "tm_p.h"
-#include "ggc.h"
-#include "target.h"
+#include "tm-constrs.h"
+#include "builtins.h"
+
+/* This file should be included last. */
#include "target-def.h"
/* Classifies a h8300_src_operand or h8300_dst_operand.
static void h8300_emit_stack_adjustment (int, HOST_WIDE_INT, bool);
static HOST_WIDE_INT round_frame_size (HOST_WIDE_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 *);
+static void h8300_print_operand_address (FILE *, machine_mode, rtx);
+static void h8300_print_operand (FILE *, rtx, int);
+static bool h8300_print_operand_punct_valid_p (unsigned char code);
#ifndef OBJECT_FORMAT_ELF
static void h8300_asm_named_section (const char *, unsigned int, tree);
#endif
+static int h8300_register_move_cost (machine_mode, reg_class_t, reg_class_t);
static int h8300_and_costs (rtx);
static int h8300_shift_costs (rtx);
static void h8300_push_pop (int, int, bool, bool);
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 unsigned int h8300_binary_length (rtx_insn *, 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 *);
static bool h8300_hard_regno_scratch_ok (unsigned int);
+static rtx h8300_get_index (rtx, machine_mode mode, int *);
/* CPU_TYPE, says what cpu we're compiling for. */
int cpu_type;
/* Initialize various cpu specific globals at start up. */
-void
-h8300_init_once (void)
+static void
+h8300_option_override (void)
{
static const char *const h8_push_ops[2] = { "push" , "push.l" };
static const char *const h8_pop_ops[2] = { "pop" , "pop.l" };
static const char *const h8_mov_ops[2] = { "mov.w", "mov.l" };
+#ifndef OBJECT_FORMAT_ELF
+ if (TARGET_H8300SX)
+ {
+ error ("%<-msx%> is not supported in coff");
+ target_flags |= MASK_H8300S;
+ }
+#endif
+
if (TARGET_H8300)
{
cpu_type = (int) CPU_H8300;
if (!TARGET_H8300S && TARGET_MAC)
{
- error ("-ms2600 is used without -ms");
+ error ("%<-ms2600%> is used without %<-ms%>");
target_flags |= MASK_H8300S_1;
}
if (TARGET_H8300 && TARGET_NORMAL_MODE)
{
- error ("-mn is used without -mh or -ms");
+ error ("%<-mn%> is used without %<-mh%> or %<-ms%> or %<-msx%>");
target_flags ^= MASK_NORMAL_MODE;
}
+ if (! TARGET_H8300S && TARGET_EXR)
+ {
+ error ("%<-mexr%> is used without %<-ms%>");
+ target_flags |= MASK_H8300S_1;
+ }
+
+ if (TARGET_H8300 && TARGET_INT32)
+ {
+ error ("%<-mint32%> is not supported for H8300 and H8300L targets");
+ target_flags ^= MASK_INT32;
+ }
+
+ if ((!TARGET_H8300S && TARGET_EXR) && (!TARGET_H8300SX && TARGET_EXR))
+ {
+ error ("%<-mexr%> is used without %<-ms%> or %<-msx%>");
+ target_flags |= MASK_H8300S_1;
+ }
+
+ if ((!TARGET_H8300S && TARGET_NEXR) && (!TARGET_H8300SX && TARGET_NEXR))
+ {
+ warning (OPT_mno_exr, "%<-mno-exr%> valid only with %<-ms%> or "
+ "%<-msx%> - Option ignored!");
+ }
+
+#ifdef H8300_LINUX
+ if ((TARGET_NORMAL_MODE))
+ {
+ error ("%<-mn%> is not supported for linux targets");
+ target_flags ^= MASK_NORMAL_MODE;
+ }
+#endif
+
/* Some of the shifts are optimized for speed by default.
See http://gcc.gnu.org/ml/gcc-patches/2002-07/msg01858.html
If optimizing for size, change shift_alg for those shift to
}
/* This target defaults to strict volatile bitfields. */
- if (flag_strict_volatile_bitfields < 0)
+ if (flag_strict_volatile_bitfields < 0 && abi_version_at_least(2))
flag_strict_volatile_bitfields = 1;
}
-/* 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. */
&& ! TREE_THIS_VOLATILE (current_function_decl) \
&& (h8300_saveall_function_p (current_function_decl) \
/* Save any call saved register that was used. */ \
- || (df_regs_ever_live_p (regno) && !call_used_regs[regno]) \
+ || (df_regs_ever_live_p (regno) \
+ && !call_used_or_fixed_reg_p (regno)) \
/* Save the frame pointer if it was used. */ \
|| (regno == HARD_FRAME_POINTER_REGNUM && df_regs_ever_live_p (regno)) \
/* Save any register used in an interrupt handler. */ \
/* Save call clobbered registers in non-leaf interrupt \
handlers. */ \
|| (h8300_current_function_interrupt_function_p () \
- && call_used_regs[regno] \
- && !current_function_is_leaf)))
+ && call_used_or_fixed_reg_p (regno) \
+ && !crtl->is_leaf)))
/* We use this to wrap all emitted insns in the prologue. */
-static rtx
-F (rtx x, bool set_it)
+static rtx_insn *
+F (rtx_insn *x, bool set_it)
{
if (set_it)
RTX_FRAME_RELATED_P (x) = 1;
int i;
for (i = 0; i < len; i++)
- F (XVECEXP (par, 0, i), true);
+ RTX_FRAME_RELATED_P (XVECEXP (par, 0, i)) = 1;
return par;
}
the splitter will do. */
if (Pmode == HImode)
{
- rtx x = emit_insn (gen_addhi3 (stack_pointer_rtx,
- stack_pointer_rtx, GEN_INT (sign * size)));
+ rtx_insn *x = emit_insn (gen_addhi3 (stack_pointer_rtx,
+ stack_pointer_rtx,
+ GEN_INT (sign * size)));
if (size < 4)
F (x, in_prologue);
}
/* Emit an insn to push register RN. */
-static void
-push (int rn)
+static rtx
+push (int rn, bool in_prologue)
{
rtx reg = gen_rtx_REG (word_mode, rn);
rtx x;
x = gen_push_h8300hs_advanced (reg);
else
x = gen_push_h8300hs_normal (reg);
- x = F (emit_insn (x), true);
- REG_NOTES (x) = gen_rtx_EXPR_LIST (REG_INC, stack_pointer_rtx, 0);
+ x = F (emit_insn (x), in_prologue);
+ add_reg_note (x, REG_INC, stack_pointer_rtx);
+ return x;
}
/* Emit an insn to pop register RN. */
-static void
+static rtx
pop (int rn)
{
rtx reg = gen_rtx_REG (word_mode, rn);
else
x = gen_pop_h8300hs_normal (reg);
x = emit_insn (x);
- REG_NOTES (x) = gen_rtx_EXPR_LIST (REG_INC, stack_pointer_rtx, 0);
+ add_reg_note (x, REG_INC, stack_pointer_rtx);
+ return x;
}
/* Emit an instruction to push or pop NREGS consecutive registers
if (pop_p)
pop (regno);
else
- push (regno);
+ push (regno, false);
return;
}
/* Add the return instruction. */
if (return_p)
{
- RTVEC_ELT (vec, i) = gen_rtx_RETURN (VOIDmode);
+ RTVEC_ELT (vec, i) = ret_rtx;
i++;
}
/* 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));
+ rhs = gen_rtx_MEM (SImode, plus_constant (Pmode, 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));
+ lhs = gen_rtx_MEM (SImode, plus_constant (Pmode, sp, (j + 1) * -4));
rhs = gen_rtx_REG (SImode, regno + j);
}
- RTVEC_ELT (vec, i + j) = gen_rtx_SET (VOIDmode, lhs, rhs);
+ RTVEC_ELT (vec, i + j) = gen_rtx_SET (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));
+ RTVEC_ELT (vec, i + j) = gen_rtx_SET (sp, gen_rtx_PLUS (Pmode, sp, offset));
x = gen_rtx_PARALLEL (VOIDmode, vec);
if (!pop_p)
return;
if (h8300_monitor_function_p (current_function_decl))
- /* My understanding of monitor functions is they act just like
- interrupt functions, except the prologue must mask
- interrupts. */
+ /* The monitor function act as normal functions, which means it
+ can accept parameters and return values. In addition to this,
+ interrupts are masked in prologue and return with "rte" in epilogue. */
emit_insn (gen_monitor_prologue ());
if (frame_pointer_needed)
{
/* Push fp. */
- push (HARD_FRAME_POINTER_REGNUM);
+ push (HARD_FRAME_POINTER_REGNUM, true);
F (emit_move_insn (hard_frame_pointer_rtx, stack_pointer_rtx), true);
}
if (TARGET_H8300S)
{
/* See how many registers we can push at the same time. */
- if ((!TARGET_H8300SX || (regno & 3) == 0)
+ if ((TARGET_H8300SX || (regno & 3) == 0)
&& ((saved_regs >> regno) & 0x0f) == 0x0f)
n_regs = 4;
- else if ((!TARGET_H8300SX || (regno & 3) == 0)
+ else if ((TARGET_H8300SX || (regno & 3) == 0)
&& ((saved_regs >> regno) & 0x07) == 0x07)
n_regs = 3;
- else if ((!TARGET_H8300SX || (regno & 1) == 0)
+ else if ((TARGET_H8300SX || (regno & 1) == 0)
&& ((saved_regs >> regno) & 0x03) == 0x03)
n_regs = 2;
}
/* Leave room for locals. */
h8300_emit_stack_adjustment (-1, round_frame_size (get_frame_size ()), true);
+
+ if (flag_stack_usage_info)
+ current_function_static_stack_size
+ = round_frame_size (get_frame_size ())
+ + (__builtin_popcount (saved_regs) * UNITS_PER_WORD)
+ + (frame_pointer_needed ? UNITS_PER_WORD : 0);
}
/* Return nonzero if we can use "rts" for the function currently being
}
if (!returned_p)
- emit_jump_insn (gen_rtx_RETURN (VOIDmode));
+ emit_jump_insn (ret_rtx);
}
/* Return nonzero if the current function is an interrupt
int
h8300_current_function_interrupt_function_p (void)
{
- return (h8300_interrupt_function_p (current_function_decl)
- || h8300_monitor_function_p (current_function_decl));
+ return (h8300_interrupt_function_p (current_function_decl));
+}
+
+int
+h8300_current_function_monitor_function_p ()
+{
+ return (h8300_monitor_function_p (current_function_decl));
}
/* Output assembly code for the start of the file. */
{
default_file_start ();
- if (TARGET_H8300H)
- fputs (TARGET_NORMAL_MODE ? "\t.h8300hn\n" : "\t.h8300h\n", asm_out_file);
- else if (TARGET_H8300SX)
+ if (TARGET_H8300SX)
fputs (TARGET_NORMAL_MODE ? "\t.h8300sxn\n" : "\t.h8300sx\n", asm_out_file);
else if (TARGET_H8300S)
fputs (TARGET_NORMAL_MODE ? "\t.h8300sn\n" : "\t.h8300s\n", asm_out_file);
+ else if (TARGET_H8300H)
+ fputs (TARGET_NORMAL_MODE ? "\t.h8300hn\n" : "\t.h8300h\n", asm_out_file);
}
/* Output assembly language code for the end of file. */
instead of adds/subs. */
void
-split_adds_subs (enum machine_mode mode, rtx *operands)
+split_adds_subs (machine_mode mode, rtx *operands)
{
HOST_WIDE_INT val = INTVAL (operands[1]);
rtx reg = operands[0];
switch (mode)
{
- case HImode:
+ case E_HImode:
gen_add = gen_addhi3;
break;
- case SImode:
+ case E_SImode:
gen_add = gen_addsi3;
break;
pragma_saveall = 1;
}
-/* If the next function argument with MODE and TYPE is to be passed in
- a register, return a reg RTX for the hard register in which to pass
- the argument. CUM represents the state after the last argument.
- If the argument is to be pushed, NULL_RTX is returned. */
+/* If the next function argument ARG is to be passed in a register, return
+ a reg RTX for the hard register in which to pass the argument. CUM
+ represents the state after the last argument. If the argument is to
+ be pushed, NULL_RTX is returned.
-rtx
-function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode,
- tree type, int named)
+ On the H8/300 all normal args are pushed, unless -mquickcall in which
+ case the first 3 arguments are passed in registers. */
+
+static rtx
+h8300_function_arg (cumulative_args_t cum_v, const function_arg_info &arg)
{
+ CUMULATIVE_ARGS *cum = get_cumulative_args (cum_v);
+
static const char *const hand_list[] = {
"__main",
"__cmpsi2",
int regpass = 0;
/* Never pass unnamed arguments in registers. */
- if (!named)
+ if (!arg.named)
return NULL_RTX;
/* Pass 3 regs worth of data in regs when user asked on the command line. */
if (regpass)
{
- int size;
-
- if (mode == BLKmode)
- size = int_size_in_bytes (type);
- else
- size = GET_MODE_SIZE (mode);
-
+ int size = arg.promoted_size_in_bytes ();
if (size + cum->nbytes <= regpass * UNITS_PER_WORD
&& cum->nbytes / UNITS_PER_WORD <= 3)
- result = gen_rtx_REG (mode, cum->nbytes / UNITS_PER_WORD);
+ result = gen_rtx_REG (arg.mode, cum->nbytes / UNITS_PER_WORD);
}
return result;
}
+
+/* Update the data in CUM to advance over argument ARG. */
+
+static void
+h8300_function_arg_advance (cumulative_args_t cum_v,
+ const function_arg_info &arg)
+{
+ CUMULATIVE_ARGS *cum = get_cumulative_args (cum_v);
+
+ cum->nbytes += ((arg.promoted_size_in_bytes () + UNITS_PER_WORD - 1)
+ & -UNITS_PER_WORD);
+}
+
\f
+/* Implements TARGET_REGISTER_MOVE_COST.
+
+ Any SI register-to-register move may need to be reloaded,
+ so inmplement h8300_register_move_cost to return > 2 so that reload never
+ shortcuts. */
+
+static int
+h8300_register_move_cost (machine_mode mode ATTRIBUTE_UNUSED,
+ reg_class_t from, reg_class_t to)
+{
+ if (from == MAC_REGS || to == MAC_REG)
+ return 6;
+ else
+ return 3;
+}
+
/* Compute the cost of an and insn. */
static int
/* Worker function for TARGET_RTX_COSTS. */
static bool
-h8300_rtx_costs (rtx x, int code, int outer_code, int *total, bool speed)
+h8300_rtx_costs (rtx x, machine_mode mode ATTRIBUTE_UNUSED, int outer_code,
+ int opno ATTRIBUTE_UNUSED, int *total, bool speed)
{
+ int code = GET_CODE (x);
+
if (TARGET_H8300SX && outer_code == MEM)
{
/* Estimate the number of execution states needed to calculate
*total = 0;
return true;
}
- if (-4 <= n || n <= 4)
+ if (n >= -4 && n <= 4)
{
switch ((int) n)
{
if (TARGET_H8300SX)
switch (GET_MODE (x))
{
- case QImode:
- case HImode:
+ case E_QImode:
+ case E_HImode:
*total = COSTS_N_INSNS (!speed ? 4 : 10);
return false;
- case SImode:
+ case E_SImode:
*total = COSTS_N_INSNS (!speed ? 4 : 18);
return false;
if (TARGET_H8300SX)
switch (GET_MODE (x))
{
- case QImode:
- case HImode:
+ case E_QImode:
+ case E_HImode:
*total = COSTS_N_INSNS (2);
return false;
- case SImode:
+ case E_SImode:
*total = COSTS_N_INSNS (5);
return false;
/* Print operand X using operand code CODE to assembly language output file
FILE. */
-void
-print_operand (FILE *file, rtx x, int code)
+static void
+h8300_print_operand (FILE *file, rtx x, int code)
{
/* This is used for communication between codes V,W,Z and Y. */
static int bitint;
switch (code)
{
+ case 'C':
+ if (h8300_constant_length (x) == 2)
+ fprintf (file, ":16");
+ else
+ fprintf (file, ":32");
+ return;
case 'E':
switch (GET_CODE (x))
{
if (GET_CODE (x) == REG)
fprintf (file, "%s%c", names_big[REGNO (x)], bitint > 7 ? 'h' : 'l');
else
- print_operand (file, x, 'R');
+ h8300_print_operand (file, x, 'R');
bitint = -1;
break;
case 'Z':
fprintf (file, "%s", names_upper_extended[REGNO (x)]);
break;
case MEM:
- print_operand (file, x, 0);
+ h8300_print_operand (file, x, 0);
break;
case CONST_INT:
fprintf (file, "#%ld", ((INTVAL (x) >> 16) & 0xffff));
case CONST_DOUBLE:
{
long val;
- REAL_VALUE_TYPE rv;
- REAL_VALUE_FROM_CONST_DOUBLE (rv, x);
- REAL_VALUE_TO_TARGET_SINGLE (rv, val);
+ REAL_VALUE_TO_TARGET_SINGLE (*CONST_DOUBLE_REAL_VALUE (x), val);
fprintf (file, "#%ld", ((val >> 16) & 0xffff));
break;
}
break;
case MEM:
x = adjust_address (x, HImode, 2);
- print_operand (file, x, 0);
+ h8300_print_operand (file, x, 0);
break;
case CONST_INT:
fprintf (file, "#%ld", INTVAL (x) & 0xffff);
case CONST_DOUBLE:
{
long val;
- REAL_VALUE_TYPE rv;
- REAL_VALUE_FROM_CONST_DOUBLE (rv, x);
- REAL_VALUE_TO_TARGET_SINGLE (rv, val);
+ REAL_VALUE_TO_TARGET_SINGLE (*CONST_DOUBLE_REAL_VALUE (x), val);
fprintf (file, "#%ld", (val & 0xffff));
break;
}
}
break;
case 'o':
- print_operand_address (file, x);
+ h8300_print_operand_address (file, VOIDmode, x);
break;
case 's':
if (GET_CODE (x) == CONST_INT)
case REG:
switch (GET_MODE (x))
{
- case QImode:
+ case E_QImode:
#if 0 /* Is it asm ("mov.b %0,r2l", ...) */
fprintf (file, "%s", byte_reg (x, 0));
#else /* ... or is it asm ("mov.b %0l,r2l", ...) */
fprintf (file, "%s", names_big[REGNO (x)]);
#endif
break;
- case HImode:
+ case E_HImode:
fprintf (file, "%s", names_big[REGNO (x)]);
break;
- case SImode:
- case SFmode:
+ case E_SImode:
+ case E_SFmode:
fprintf (file, "%s", names_extended[REGNO (x)]);
break;
default:
rtx addr = XEXP (x, 0);
fprintf (file, "@");
- output_address (addr);
+ output_address (GET_MODE (x), addr);
/* Add a length suffix to constant addresses. Although this
is often unnecessary, it helps to avoid ambiguity in the
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. */
+ /* FALLTHRU */
+
+ /* 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 CONST:
case LABEL_REF:
fprintf (file, "#");
- print_operand_address (file, x);
+ h8300_print_operand_address (file, VOIDmode, x);
break;
case CONST_DOUBLE:
{
long val;
- REAL_VALUE_TYPE rv;
- REAL_VALUE_FROM_CONST_DOUBLE (rv, x);
- REAL_VALUE_TO_TARGET_SINGLE (rv, val);
+ REAL_VALUE_TO_TARGET_SINGLE (*CONST_DOUBLE_REAL_VALUE (x), val);
fprintf (file, "#%ld", val);
break;
}
}
}
+/* Implements TARGET_PRINT_OPERAND_PUNCT_VALID_P. */
+
+static bool
+h8300_print_operand_punct_valid_p (unsigned char code)
+{
+ return (code == '#');
+}
+
/* Output assembly language output for the address ADDR to FILE. */
-void
-print_operand_address (FILE *file, rtx addr)
+static void
+h8300_print_operand_address (FILE *file, machine_mode mode, rtx addr)
{
rtx index;
int size;
if (GET_CODE (index) == REG)
{
/* reg,foo */
- print_operand_address (file, XEXP (addr, 1));
+ h8300_print_operand_address (file, mode, XEXP (addr, 1));
fprintf (file, ",");
switch (size)
{
case 0:
- print_operand_address (file, index);
+ h8300_print_operand_address (file, mode, index);
break;
case 1:
- print_operand (file, index, 'X');
+ h8300_print_operand (file, index, 'X');
fputs (".b", file);
break;
case 2:
- print_operand (file, index, 'T');
+ h8300_print_operand (file, index, 'T');
fputs (".w", file);
break;
case 4:
- print_operand (file, index, 'S');
+ h8300_print_operand (file, index, 'S');
fputs (".l", file);
break;
}
- /* print_operand_address (file, XEXP (addr, 0)); */
+ /* h8300_print_operand_address (file, XEXP (addr, 0)); */
}
else
{
/* foo+k */
- print_operand_address (file, XEXP (addr, 0));
+ h8300_print_operand_address (file, mode, XEXP (addr, 0));
fprintf (file, "+");
- print_operand_address (file, XEXP (addr, 1));
+ h8300_print_operand_address (file, mode, XEXP (addr, 1));
}
fprintf (file, ")");
break;
option. */
void
-final_prescan_insn (rtx insn, rtx *operand ATTRIBUTE_UNUSED,
+final_prescan_insn (rtx_insn *insn, rtx *operand ATTRIBUTE_UNUSED,
int num_operands ATTRIBUTE_UNUSED)
{
/* This holds the last insn address. */
return (to == STACK_POINTER_REGNUM ? ! frame_pointer_needed : true);
}
+/* Conditionally modify register usage based on target flags. */
+
+static void
+h8300_conditional_register_usage (void)
+{
+ if (!TARGET_MAC)
+ fixed_regs[MAC_REG] = call_used_regs[MAC_REG] = 1;
+}
+
/* Function for INITIAL_ELIMINATION_OFFSET(FROM, TO, OFFSET).
Define the offset between two registers, one to be eliminated, and
the other its replacement, at the start of a routine. */
else
ret = gen_rtx_MEM (Pmode,
memory_address (Pmode,
- plus_constant (frame, UNITS_PER_WORD)));
+ plus_constant (Pmode, frame,
+ UNITS_PER_WORD)));
set_mem_alias_set (ret, get_frame_alias_set ());
return ret;
}
/* Update the condition code from the insn. */
void
-notice_update_cc (rtx body, rtx insn)
+notice_update_cc (rtx body, rtx_insn *insn)
{
rtx set;
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. */
-rtx
-h8300_get_index (rtx x, enum machine_mode mode, int *size)
+static rtx
+h8300_get_index (rtx x, machine_mode mode, int *size)
{
int dummy, factor;
return x;
}
\f
+/* Worker function for TARGET_MODE_DEPENDENT_ADDRESS_P.
+
+ On the H8/300, the predecrement and postincrement address depend thus
+ (the amount of decrement or increment being the length of the operand). */
+
+static bool
+h8300_mode_dependent_address_p (const_rtx addr,
+ addr_space_t as ATTRIBUTE_UNUSED)
+{
+ if (GET_CODE (addr) == PLUS
+ && h8300_get_index (XEXP (addr, 0), VOIDmode, 0) != XEXP (addr, 0))
+ return true;
+
+ return false;
+}
+\f
static const h8300_length_table addb_length_table =
{
/* #xx Rs @aa @Rs @xx */
/* Calculate the length of general binary instruction INSN using TABLE. */
static unsigned int
-h8300_binary_length (rtx insn, const h8300_length_table *table)
+h8300_binary_length (rtx_insn *insn, const h8300_length_table *table)
{
rtx set;
OPERANDS is the array of its operands. */
unsigned int
-h8300_insn_length_from_table (rtx insn, rtx * operands)
+h8300_insn_length_from_table (rtx_insn *insn, rtx * operands)
{
switch (get_attr_length_table (insn))
{
case LENGTH_TABLE_NONE:
gcc_unreachable ();
- 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_ADD:
+ if (GET_MODE (operands[0]) == QImode)
+ return h8300_binary_length (insn, &addb_length_table);
+ else if (GET_MODE (operands[0]) == HImode)
+ return h8300_binary_length (insn, &addw_length_table);
+ else if (GET_MODE (operands[0]) == SImode)
+ return h8300_binary_length (insn, &addl_length_table);
+ gcc_unreachable ();
case LENGTH_TABLE_LOGICB:
return h8300_binary_length (insn, &logicb_length_table);
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));
+ set_mem_size (first_dest, n & -factor);
+ set_mem_size (first_src, 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)));
void
h8300_swap_into_er6 (rtx addr)
{
- push (HARD_FRAME_POINTER_REGNUM);
+ rtx insn = push (HARD_FRAME_POINTER_REGNUM, false);
+ if (frame_pointer_needed)
+ add_reg_note (insn, REG_CFA_DEF_CFA,
+ plus_constant (Pmode, gen_rtx_MEM (Pmode, stack_pointer_rtx),
+ 2 * UNITS_PER_WORD));
+ else
+ add_reg_note (insn, REG_CFA_ADJUST_CFA,
+ gen_rtx_SET (stack_pointer_rtx,
+ plus_constant (Pmode, stack_pointer_rtx, 4)));
+
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,
+ plus_constant (Pmode, hard_frame_pointer_rtx,
GET_MODE_SIZE (word_mode)));
}
void
h8300_swap_out_of_er6 (rtx addr)
{
+ rtx insn;
+
if (REGNO (addr) != SP_REG)
emit_move_insn (addr, hard_frame_pointer_rtx);
- pop (HARD_FRAME_POINTER_REGNUM);
+
+ insn = pop (HARD_FRAME_POINTER_REGNUM);
+ if (frame_pointer_needed)
+ add_reg_note (insn, REG_CFA_DEF_CFA,
+ plus_constant (Pmode, hard_frame_pointer_rtx,
+ 2 * UNITS_PER_WORD));
+ else
+ add_reg_note (insn, REG_CFA_ADJUST_CFA,
+ gen_rtx_SET (stack_pointer_rtx,
+ plus_constant (Pmode, stack_pointer_rtx, -4)));
}
\f
/* Return the length of mov instruction. */
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]);
+ machine_mode mode = GET_MODE (operands[0]);
rtx dest = operands[0];
rtx src = operands[1];
rtx addr;
switch (mode)
{
- case QImode:
+ case E_QImode:
if (addr == NULL_RTX)
return 2;
base_length = 4;
break;
- case HImode:
+ case E_HImode:
if (addr == NULL_RTX)
{
if (REG_P (src))
base_length = 4;
break;
- case SImode:
+ case E_SImode:
if (addr == NULL_RTX)
{
if (REG_P (src))
base_length = 8;
break;
- case SFmode:
+ case E_SFmode:
if (addr == NULL_RTX)
{
if (REG_P (src))
return 4;
- if (CONST_DOUBLE_OK_FOR_LETTER_P (src, 'G'))
+ if (satisfies_constraint_G (src))
return 4;
return 8;
switch (mode)
{
- case QImode:
+ case E_QImode:
if (addr == NULL_RTX)
return 2;
base_length = 8;
break;
- case HImode:
+ case E_HImode:
if (addr == NULL_RTX)
{
if (REG_P (src))
base_length = 8;
break;
- case SImode:
+ case E_SImode:
if (addr == NULL_RTX)
{
if (REG_P (src))
base_length = 10;
break;
- case SFmode:
+ case E_SFmode:
if (addr == NULL_RTX)
{
if (REG_P (src))
return 2;
- if (CONST_DOUBLE_OK_FOR_LETTER_P (src, 'G'))
+ if (satisfies_constraint_G (src))
return 2;
return 6;
const char *
output_plussi (rtx *operands)
{
- enum machine_mode mode = GET_MODE (operands[0]);
+ machine_mode mode = GET_MODE (operands[0]);
gcc_assert (mode == SImode);
unsigned int
compute_plussi_length (rtx *operands)
{
- enum machine_mode mode = GET_MODE (operands[0]);
+ machine_mode mode = GET_MODE (operands[0]);
gcc_assert (mode == SImode);
/* Compute which flag bits are valid after an addition insn. */
-int
+enum attr_cc
compute_plussi_cc (rtx *operands)
{
- enum machine_mode mode = GET_MODE (operands[0]);
+ machine_mode mode = GET_MODE (operands[0]);
gcc_assert (mode == SImode);
/* Output a logical insn. */
const char *
-output_logical_op (enum machine_mode mode, rtx *operands)
+output_logical_op (machine_mode mode, rtx *operands)
{
/* Figure out the logical op that we need to perform. */
enum rtx_code code = GET_CODE (operands[3]);
switch (mode)
{
- case HImode:
+ case E_HImode:
/* First, see if we can finish with one insn. */
if ((TARGET_H8300H || TARGET_H8300S)
&& b0 != 0
}
}
break;
- case SImode:
+ case E_SImode:
if (TARGET_H8300H || TARGET_H8300S)
{
/* Determine if the lower half can be taken care of in no more
/* Compute the length of a logical insn. */
unsigned int
-compute_logical_op_length (enum machine_mode mode, rtx *operands)
+compute_logical_op_length (machine_mode mode, rtx *operands)
{
/* Figure out the logical op that we need to perform. */
enum rtx_code code = GET_CODE (operands[3]);
switch (mode)
{
- case HImode:
+ case E_HImode:
/* First, see if we can finish with one insn. */
if ((TARGET_H8300H || TARGET_H8300S)
&& b0 != 0
length += 2;
}
break;
- case SImode:
+ case E_SImode:
if (TARGET_H8300H || TARGET_H8300S)
{
/* Determine if the lower half can be taken care of in no more
/* Compute which flag bits are valid after a logical insn. */
-int
-compute_logical_op_cc (enum machine_mode mode, rtx *operands)
+enum attr_cc
+compute_logical_op_cc (machine_mode mode, rtx *operands)
{
/* Figure out the logical op that we need to perform. */
enum rtx_code code = GET_CODE (operands[3]);
switch (mode)
{
- case HImode:
+ case E_HImode:
/* First, see if we can finish with one insn. */
if ((TARGET_H8300H || TARGET_H8300S)
&& b0 != 0
cc = CC_SET_ZNV;
}
break;
- case SImode:
+ case E_SImode:
if (TARGET_H8300H || TARGET_H8300S)
{
/* Determine if the lower half can be taken care of in no more
rtx tmp;
tmp = gen_rtx_COMPARE (VOIDmode, op0, op1);
- emit_insn (gen_rtx_SET (VOIDmode, cc0_rtx, tmp));
+ emit_insn (gen_rtx_SET (cc0_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));
+ emit_jump_insn (gen_rtx_SET (pc_rtx, tmp));
}
rtx tmp;
tmp = gen_rtx_COMPARE (VOIDmode, op0, op1);
- emit_insn (gen_rtx_SET (VOIDmode, cc0_rtx, tmp));
+ emit_insn (gen_rtx_SET (cc0_rtx, tmp));
tmp = gen_rtx_fmt_ee (code, GET_MODE (dest), cc0_rtx, const0_rtx);
- emit_insn (gen_rtx_SET (VOIDmode, dest, tmp));
+ emit_insn (gen_rtx_SET (dest, tmp));
}
\f
/* Shifts.
/* 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)
+h8sx_classify_shift (machine_mode mode, enum rtx_code code, rtx op)
{
if (!TARGET_H8300SX)
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
+ size suffix ('b', 'w' or 'l') and OPTYPE is the h8300_print_operand
prefix for the destination operand. */
const char *
/* Emit code to do shifts. */
bool
-expand_a_shift (enum machine_mode mode, int code, rtx operands[])
+expand_a_shift (machine_mode mode, enum rtx_code code, rtx operands[])
{
switch (h8sx_classify_shift (mode, code, operands[2]))
{
emit_insn (gen_rtx_PARALLEL
(VOIDmode,
gen_rtvec (2,
- gen_rtx_SET (VOIDmode, copy_rtx (operands[0]),
+ gen_rtx_SET (copy_rtx (operands[0]),
gen_rtx_fmt_ee (code, mode,
copy_rtx (operands[0]), operands[2])),
gen_rtx_CLOBBER (VOIDmode,
struct shift_insn
{
const char *const assembler;
- const int cc_valid;
+ const enum attr_cc cc_valid;
};
/* Assembler instruction shift table.
const char *shift2;
/* CC status for SHIFT_INLINE. */
- int cc_inline;
+ enum attr_cc cc_inline;
/* CC status for SHIFT_SPECIAL. */
- int cc_special;
+ enum attr_cc cc_special;
};
static void get_shift_alg (enum shift_type,
/* Find the target CPU. */
if (TARGET_H8300)
cpu = H8_300;
- else if (TARGET_H8300H)
- cpu = H8_300H;
- else
+ else if (TARGET_H8300S)
cpu = H8_S;
+ else
+ cpu = H8_300H;
/* Find the shift algorithm. */
info->alg = SHIFT_LOOP;
goto end;
}
}
- else if ((8 <= count && count <= 13)
+ else if ((count >= 8 && count <= 13)
|| (TARGET_H8300S && count == 14))
{
info->remainder = count - 8;
gcc_unreachable ();
case SIshift:
- if (TARGET_H8300 && 8 <= count && count <= 9)
+ if (TARGET_H8300 && count >= 8 && count <= 9)
{
info->remainder = count - 8;
gcc_unreachable ();
}
}
- else if ((TARGET_H8300 && 16 <= count && count <= 20)
- || (TARGET_H8300H && 16 <= count && count <= 19)
- || (TARGET_H8300S && 16 <= count && count <= 21))
+ else if ((TARGET_H8300 && count >= 16 && count <= 20)
+ || (TARGET_H8300H && count >= 16 && count <= 19)
+ || (TARGET_H8300S && count >= 16 && count <= 21))
{
info->remainder = count - 16;
goto end;
}
}
- else if (TARGET_H8300 && 24 <= count && count <= 28)
+ else if (TARGET_H8300 && count >= 24 && count <= 28)
{
info->remainder = count - 24;
info->cc_inline = CC_SET_ZNV;
goto end;
case SHIFT_ASHIFTRT:
- info->special = "mov.b\t%z0,%w0\n\tbld\t#7,%w0\n\tsubx\t%x0,%x0\n\tsubx\t%x0,%x0\n\tsubx\t%x0,%x0";
+ info->special = "mov.b\t%z0,%w0\n\tbld\t#7,%w0\n\tsubx\t%x0,%x0\n\tsubx\t%y0,%y0\n\tsubx\t%z0,%z0";
info->shift1 = "shar.b\t%w0";
info->cc_inline = CC_SET_ZNV;
goto end;
}
}
else if ((TARGET_H8300H && count == 24)
- || (TARGET_H8300S && 24 <= count && count <= 25))
+ || (TARGET_H8300S && count >= 24 && count <= 25))
{
info->remainder = count - 24;
needed for some shift with COUNT and MODE. Return 0 otherwise. */
int
-h8300_shift_needs_scratch_p (int count, enum machine_mode mode)
+h8300_shift_needs_scratch_p (int count, machine_mode mode)
{
enum h8_cpu cpu;
int a, lr, ar;
/* Find out the target CPU. */
if (TARGET_H8300)
cpu = H8_300;
- else if (TARGET_H8300H)
- cpu = H8_300H;
- else
+ else if (TARGET_H8300S)
cpu = H8_S;
+ else
+ cpu = H8_300H;
/* Find the shift algorithm. */
switch (mode)
{
- case QImode:
+ case E_QImode:
a = shift_alg_qi[cpu][SHIFT_ASHIFT][count];
lr = shift_alg_qi[cpu][SHIFT_LSHIFTRT][count];
ar = shift_alg_qi[cpu][SHIFT_ASHIFTRT][count];
break;
- case HImode:
+ case E_HImode:
a = shift_alg_hi[cpu][SHIFT_ASHIFT][count];
lr = shift_alg_hi[cpu][SHIFT_LSHIFTRT][count];
ar = shift_alg_hi[cpu][SHIFT_ASHIFTRT][count];
break;
- case SImode:
+ case E_SImode:
a = shift_alg_si[cpu][SHIFT_ASHIFT][count];
lr = shift_alg_si[cpu][SHIFT_LSHIFTRT][count];
ar = shift_alg_si[cpu][SHIFT_ASHIFTRT][count];
{
static int loopend_lab;
rtx shift = operands[3];
- enum machine_mode mode = GET_MODE (shift);
+ machine_mode mode = GET_MODE (shift);
enum rtx_code code = GET_CODE (shift);
enum shift_type shift_type;
enum shift_mode shift_mode;
switch (mode)
{
- case QImode:
+ case E_QImode:
shift_mode = QIshift;
break;
- case HImode:
+ case E_HImode:
shift_mode = HIshift;
break;
- case SImode:
+ case E_SImode:
shift_mode = SIshift;
break;
default:
/* Now mask off the high bits. */
switch (mode)
{
- case QImode:
+ case E_QImode:
sprintf (insn_buf, "and\t#%d,%%X0", mask);
break;
- case HImode:
+ case E_HImode:
gcc_assert (TARGET_H8300H || TARGET_H8300S);
sprintf (insn_buf, "and.w\t#%d,%%T0", mask);
break;
compute_a_shift_length (rtx insn ATTRIBUTE_UNUSED, rtx *operands)
{
rtx shift = operands[3];
- enum machine_mode mode = GET_MODE (shift);
+ machine_mode mode = GET_MODE (shift);
enum rtx_code code = GET_CODE (shift);
enum shift_type shift_type;
enum shift_mode shift_mode;
switch (mode)
{
- case QImode:
+ case E_QImode:
shift_mode = QIshift;
break;
- case HImode:
+ case E_HImode:
shift_mode = HIshift;
break;
- case SImode:
+ case E_SImode:
shift_mode = SIshift;
break;
default:
/* Now mask off the high bits. */
switch (mode)
{
- case QImode:
+ case E_QImode:
wlength += 1;
break;
- case HImode:
+ case E_HImode:
wlength += 2;
break;
- case SImode:
+ case E_SImode:
gcc_assert (!TARGET_H8300);
wlength += 3;
break;
/* Compute which flag bits are valid after a shift insn. */
-int
+enum attr_cc
compute_a_shift_cc (rtx insn ATTRIBUTE_UNUSED, rtx *operands)
{
rtx shift = operands[3];
- enum machine_mode mode = GET_MODE (shift);
+ machine_mode mode = GET_MODE (shift);
enum rtx_code code = GET_CODE (shift);
enum shift_type shift_type;
enum shift_mode shift_mode;
switch (mode)
{
- case QImode:
+ case E_QImode:
shift_mode = QIshift;
break;
- case HImode:
+ case E_HImode:
shift_mode = HIshift;
break;
- case SImode:
+ case E_SImode:
shift_mode = SIshift;
break;
default:
rtx dst = operands[0];
rtx src = operands[1];
rtx rotate_amount = operands[2];
- enum machine_mode mode = GET_MODE (dst);
+ machine_mode mode = GET_MODE (dst);
if (h8sx_classify_shift (mode, ROTATE, rotate_amount) == H8SX_SHIFT_UNARY)
return false;
if (GET_CODE (rotate_amount) != CONST_INT)
{
rtx counter = gen_reg_rtx (QImode);
- rtx start_label = gen_label_rtx ();
- rtx end_label = gen_label_rtx ();
+ rtx_code_label *start_label = gen_label_rtx ();
+ rtx_code_label *end_label = gen_label_rtx ();
/* If the rotate amount is less than or equal to 0,
we go out of the loop. */
/* Rotate by one bit. */
switch (mode)
{
- case QImode:
+ case E_QImode:
emit_insn (gen_rotlqi3_1 (dst, dst, const1_rtx));
break;
- case HImode:
+ case E_HImode:
emit_insn (gen_rotlhi3_1 (dst, dst, const1_rtx));
break;
- case SImode:
+ case E_SImode:
emit_insn (gen_rotlsi3_1 (dst, dst, const1_rtx));
break;
default:
/* Rotate by AMOUNT bits. */
switch (mode)
{
- case QImode:
+ case E_QImode:
emit_insn (gen_rotlqi3_1 (dst, dst, rotate_amount));
break;
- case HImode:
+ case E_HImode:
emit_insn (gen_rotlhi3_1 (dst, dst, rotate_amount));
break;
- case SImode:
+ case E_SImode:
emit_insn (gen_rotlsi3_1 (dst, dst, rotate_amount));
break;
default:
const char *insn_buf;
int bits;
int amount;
- enum machine_mode mode = GET_MODE (dst);
+ machine_mode mode = GET_MODE (dst);
gcc_assert (GET_CODE (rotate_amount) == CONST_INT);
switch (mode)
{
- case QImode:
+ case E_QImode:
rotate_mode = QIshift;
break;
- case HImode:
+ case E_HImode:
rotate_mode = HIshift;
break;
- case SImode:
+ case E_SImode:
rotate_mode = SIshift;
break;
default:
{
switch (mode)
{
- case HImode:
+ case E_HImode:
/* This code works on any family. */
insn_buf = "xor.b\t%s0,%t0\n\txor.b\t%t0,%s0\n\txor.b\t%s0,%t0";
output_asm_insn (insn_buf, operands);
break;
- case SImode:
+ case E_SImode:
/* This code works on the H8/300H and H8S. */
insn_buf = "xor.w\t%e0,%f0\n\txor.w\t%f0,%e0\n\txor.w\t%e0,%f0";
output_asm_insn (insn_buf, operands);
{
rtx src = operands[1];
rtx amount_rtx = operands[2];
- enum machine_mode mode = GET_MODE (src);
+ machine_mode mode = GET_MODE (src);
int amount;
unsigned int length = 0;
{
/* OK to have a memory dest. */
if (GET_CODE (operands[0]) == MEM
- && !OK_FOR_U (operands[0]))
+ && !satisfies_constraint_U (operands[0]))
{
rtx mem = gen_rtx_MEM (GET_MODE (operands[0]),
copy_to_mode_reg (Pmode,
}
if (GET_CODE (operands[1]) == MEM
- && !OK_FOR_U (operands[1]))
+ && !satisfies_constraint_U (operands[1]))
{
rtx mem = gen_rtx_MEM (GET_MODE (operands[1]),
copy_to_mode_reg (Pmode,
static const struct attribute_spec h8300_attribute_table[] =
{
- /* { 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 },
- { "eightbit_data", 0, 0, true, false, false, h8300_handle_eightbit_data_attribute },
- { "tiny_data", 0, 0, true, false, false, h8300_handle_tiny_data_attribute },
- { NULL, 0, 0, false, false, false, NULL }
+ /* { name, min_len, max_len, decl_req, type_req, fn_type_req,
+ affects_type_identity, handler, exclude } */
+ { "interrupt_handler", 0, 0, true, false, false, false,
+ h8300_handle_fndecl_attribute, NULL },
+ { "saveall", 0, 0, true, false, false, false,
+ h8300_handle_fndecl_attribute, NULL },
+ { "OS_Task", 0, 0, true, false, false, false,
+ h8300_handle_fndecl_attribute, NULL },
+ { "monitor", 0, 0, true, false, false, false,
+ h8300_handle_fndecl_attribute, NULL },
+ { "function_vector", 0, 0, true, false, false, false,
+ h8300_handle_fndecl_attribute, NULL },
+ { "eightbit_data", 0, 0, true, false, false, false,
+ h8300_handle_eightbit_data_attribute, NULL },
+ { "tiny_data", 0, 0, true, false, false, false,
+ h8300_handle_tiny_data_attribute, NULL },
+ { NULL, 0, 0, false, false, false, false, NULL, NULL }
};
if (TREE_STATIC (decl) || DECL_EXTERNAL (decl))
{
- DECL_SECTION_NAME (decl) = build_string (7, ".eight");
+ set_decl_section_name (decl, ".eight");
}
else
{
if (TREE_STATIC (decl) || DECL_EXTERNAL (decl))
{
- DECL_SECTION_NAME (decl) = build_string (6, ".tiny");
+ set_decl_section_name (decl, ".tiny");
}
else
{
if (GET_CODE (x) == SYMBOL_REF)
return (SYMBOL_REF_FLAGS (x) & SYMBOL_FLAG_EIGHTBIT_DATA) != 0;
+ if (GET_CODE (x) == CONST
+ && GET_CODE (XEXP (x, 0)) == PLUS
+ && GET_CODE (XEXP (XEXP (x, 0), 0)) == SYMBOL_REF
+ && (SYMBOL_REF_FLAGS (XEXP (XEXP (x, 0), 0)) & SYMBOL_FLAG_EIGHTBIT_DATA) != 0)
+ return 1;
+
if (GET_CODE (x) != CONST_INT)
return 0;
before I3. I3 is assumed to be a comparison insn. */
int
-same_cmp_preceding_p (rtx i3)
+same_cmp_preceding_p (rtx_insn *i3)
{
- rtx i1, i2;
+ rtx_insn *i1, *i2;
/* Make sure we have a sequence of three insns. */
i2 = prev_nonnote_insn (i3);
- if (i2 == NULL_RTX)
+ if (i2 == NULL)
return 0;
i1 = prev_nonnote_insn (i2);
- if (i1 == NULL_RTX)
+ if (i1 == NULL)
return 0;
return (INSN_P (i1) && rtx_equal_p (PATTERN (i1), PATTERN (i3))
after I1. I1 is assumed to be a comparison insn. */
int
-same_cmp_following_p (rtx i1)
+same_cmp_following_p (rtx_insn *i1)
{
- rtx i2, i3;
+ rtx_insn *i2, *i3;
/* Make sure we have a sequence of three insns. */
i2 = next_nonnote_insn (i1);
- if (i2 == NULL_RTX)
+ if (i2 == NULL)
return 0;
i3 = next_nonnote_insn (i2);
- if (i3 == NULL_RTX)
+ if (i3 == NULL)
return 0;
return (INSN_P (i3) && rtx_equal_p (PATTERN (i1), PATTERN (i3))
}
-/* 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
CONSTANT_ADDRESS. */
static bool
-h8300_legitimate_address_p (enum machine_mode mode, rtx x, bool strict)
+h8300_legitimate_address_p (machine_mode mode, rtx x, bool strict)
{
/* The register indirect addresses like @er0 is always valid. */
if (h8300_rtx_ok_for_base_p (x, strict))
return 0;
}
-/* Worker function for HARD_REGNO_NREGS.
+/* Implement TARGET_HARD_REGNO_MODE_OK. */
- 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)
+static bool
+h8300_hard_regno_mode_ok (unsigned int regno, machine_mode mode)
{
if (TARGET_H8300)
/* If an even reg, then anything goes. Otherwise the mode must be
goes. */
return regno == MAC_REG ? mode == SImode : 1;
}
+
+/* Implement TARGET_MODES_TIEABLE_P. */
+
+static bool
+h8300_modes_tieable_p (machine_mode mode1, machine_mode mode2)
+{
+ return (mode1 == mode2
+ || ((mode1 == QImode
+ || mode1 == HImode
+ || ((TARGET_H8300H || TARGET_H8300S) && mode1 == SImode))
+ && (mode2 == QImode
+ || mode2 == HImode
+ || ((TARGET_H8300H || TARGET_H8300S) && mode2 == SImode))));
+}
+
+/* Helper function for the move patterns. Make sure a move is legitimate. */
+
+bool
+h8300_move_ok (rtx dest, rtx src)
+{
+ rtx addr, other;
+
+ /* Validate that at least one operand is a register. */
+ if (MEM_P (dest))
+ {
+ if (MEM_P (src) || CONSTANT_P (src))
+ return false;
+ addr = XEXP (dest, 0);
+ other = src;
+ }
+ else if (MEM_P (src))
+ {
+ addr = XEXP (src, 0);
+ other = dest;
+ }
+ else
+ return true;
+
+ /* Validate that auto-inc doesn't affect OTHER. */
+ if (GET_RTX_CLASS (GET_CODE (addr)) != RTX_AUTOINC)
+ return true;
+ addr = XEXP (addr, 0);
+
+ if (addr == stack_pointer_rtx)
+ return register_no_sp_elim_operand (other, VOIDmode);
+ else
+ return !reg_overlap_mentioned_p(other, addr);
+}
\f
/* Perform target dependent optabs initialization. */
static void
set_optab_libfunc (umod_optab, HImode, "__umodhi3");
}
\f
+/* Worker function for TARGET_FUNCTION_VALUE.
+
+ On the H8 the return value is in R0/R1. */
+
+static rtx
+h8300_function_value (const_tree ret_type,
+ const_tree fn_decl_or_type ATTRIBUTE_UNUSED,
+ bool outgoing ATTRIBUTE_UNUSED)
+{
+ return gen_rtx_REG (TYPE_MODE (ret_type), R0_REG);
+}
+
+/* Worker function for TARGET_LIBCALL_VALUE.
+
+ On the H8 the return value is in R0/R1. */
+
+static rtx
+h8300_libcall_value (machine_mode mode, const_rtx fun ATTRIBUTE_UNUSED)
+{
+ return gen_rtx_REG (mode, R0_REG);
+}
+
+/* Worker function for TARGET_FUNCTION_VALUE_REGNO_P.
+
+ On the H8, R0 is the only register thus used. */
+
+static bool
+h8300_function_value_regno_p (const unsigned int regno)
+{
+ return (regno == R0_REG);
+}
+
/* Worker function for TARGET_RETURN_IN_MEMORY. */
static bool
emit_move_insn (mem, tem);
}
}
+
+/* Implement PUSH_ROUNDING.
+
+ On the H8/300, @-sp really pushes a byte if you ask it to - but that's
+ dangerous, so we claim that it always pushes a word, then we catch
+ the mov.b rx,@-sp and turn it into a mov.w rx,@-sp on output.
+
+ On the H8/300H, we simplify TARGET_QUICKCALL by setting this to 4
+ and doing a similar thing. */
+
+poly_int64
+h8300_push_rounding (poly_int64 bytes)
+{
+ return ((bytes + PARM_BOUNDARY / 8 - 1) & (-PARM_BOUNDARY / 8));
+}
\f
/* Initialize the GCC target structure. */
#undef TARGET_ATTRIBUTE_TABLE
#undef TARGET_ASM_FILE_END
#define TARGET_ASM_FILE_END h8300_file_end
+#undef TARGET_PRINT_OPERAND
+#define TARGET_PRINT_OPERAND h8300_print_operand
+#undef TARGET_PRINT_OPERAND_ADDRESS
+#define TARGET_PRINT_OPERAND_ADDRESS h8300_print_operand_address
+#undef TARGET_PRINT_OPERAND_PUNCT_VALID_P
+#define TARGET_PRINT_OPERAND_PUNCT_VALID_P h8300_print_operand_punct_valid_p
+
#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_REGISTER_MOVE_COST
+#define TARGET_REGISTER_MOVE_COST h8300_register_move_cost
+
#undef TARGET_RTX_COSTS
#define TARGET_RTX_COSTS h8300_rtx_costs
#undef TARGET_INIT_LIBFUNCS
#define TARGET_INIT_LIBFUNCS h8300_init_libfuncs
+#undef TARGET_FUNCTION_VALUE
+#define TARGET_FUNCTION_VALUE h8300_function_value
+
+#undef TARGET_LIBCALL_VALUE
+#define TARGET_LIBCALL_VALUE h8300_libcall_value
+
+#undef TARGET_FUNCTION_VALUE_REGNO_P
+#define TARGET_FUNCTION_VALUE_REGNO_P h8300_function_value_regno_p
+
#undef TARGET_RETURN_IN_MEMORY
#define TARGET_RETURN_IN_MEMORY h8300_return_in_memory
+#undef TARGET_FUNCTION_ARG
+#define TARGET_FUNCTION_ARG h8300_function_arg
+
+#undef TARGET_FUNCTION_ARG_ADVANCE
+#define TARGET_FUNCTION_ARG_ADVANCE h8300_function_arg_advance
+
#undef TARGET_MACHINE_DEPENDENT_REORG
#define TARGET_MACHINE_DEPENDENT_REORG h8300_reorg
#undef TARGET_HARD_REGNO_SCRATCH_OK
#define TARGET_HARD_REGNO_SCRATCH_OK h8300_hard_regno_scratch_ok
+#undef TARGET_HARD_REGNO_MODE_OK
+#define TARGET_HARD_REGNO_MODE_OK h8300_hard_regno_mode_ok
+
+#undef TARGET_MODES_TIEABLE_P
+#define TARGET_MODES_TIEABLE_P h8300_modes_tieable_p
+
+#undef TARGET_LRA_P
+#define TARGET_LRA_P hook_bool_void_false
+
#undef TARGET_LEGITIMATE_ADDRESS_P
#define TARGET_LEGITIMATE_ADDRESS_P h8300_legitimate_address_p
-#undef TARGET_DEFAULT_TARGET_FLAGS
-#define TARGET_DEFAULT_TARGET_FLAGS TARGET_DEFAULT
-
#undef TARGET_CAN_ELIMINATE
#define TARGET_CAN_ELIMINATE h8300_can_eliminate
+#undef TARGET_CONDITIONAL_REGISTER_USAGE
+#define TARGET_CONDITIONAL_REGISTER_USAGE h8300_conditional_register_usage
+
#undef TARGET_TRAMPOLINE_INIT
#define TARGET_TRAMPOLINE_INIT h8300_trampoline_init
+#undef TARGET_OPTION_OVERRIDE
+#define TARGET_OPTION_OVERRIDE h8300_option_override
+
+#undef TARGET_MODE_DEPENDENT_ADDRESS_P
+#define TARGET_MODE_DEPENDENT_ADDRESS_P h8300_mode_dependent_address_p
+
+#undef TARGET_HAVE_SPECULATION_SAFE_VALUE
+#define TARGET_HAVE_SPECULATION_SAFE_VALUE speculation_safe_value_not_needed
+
struct gcc_target targetm = TARGET_INITIALIZER;