OpenCores
URL https://opencores.org/ocsvn/openrisc_me/openrisc_me/trunk

Subversion Repositories openrisc_me

Compare Revisions

  • This comparison shows the changes necessary to convert path
    /openrisc/trunk/gnu-src/gcc-4.2.2/gcc/config/h8300
    from Rev 38 to Rev 154
    Reverse comparison

Rev 38 → Rev 154

/crti.asm
0,0 → 1,68
/* Copyright (C) 2001, 2002 Free Software Foundation, Inc.
 
This file is part of GCC.
 
GCC is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the
Free Software Foundation; either version 2, or (at your option) any
later version.
 
In addition to the permissions in the GNU General Public License, the
Free Software Foundation gives you unlimited permission to link the
compiled version of this file into combinations with other programs,
and to distribute those combinations without any restriction coming
from the use of this file. (The General Public License restrictions
do apply in other respects; for example, they cover modification of
the file, and distribution when not linked into a combine
executable.)
 
GCC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
 
You should have received a copy of the GNU General Public License
along with this program; see the file COPYING. If not, write to
the Free Software Foundation, 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA. */
 
/* The code in sections .init and .fini is supposed to be a single
regular function. The function in .init is called directly from
start in crt0.asm. The function in .fini is atexit()ed in crt0.asm
too.
 
crti.asm contributes the prologue of a function to these sections,
and crtn.asm comes up the epilogue. STARTFILE_SPEC should list
crti.o before any other object files that might add code to .init
or .fini sections, and ENDFILE_SPEC should list crtn.o after any
such object files. */
 
#ifdef __H8300H__
#ifdef __NORMAL_MODE__
.h8300hn
#else
.h8300h
#endif
#endif
 
#ifdef __H8300S__
#ifdef __NORMAL_MODE__
.h8300sn
#else
.h8300s
#endif
#endif
#ifdef __H8300SX__
#ifdef __NORMAL_MODE__
.h8300sxn
#else
.h8300sx
#endif
#endif
 
.section .init
.global __init
__init:
.section .fini
.global __fini
__fini:
/rtems.h
0,0 → 1,29
/* Definitions for rtems targeting a H8
Copyright (C) 1996, 1997, 2000, 2002, 2003, 2007 Free Software Foundation, Inc.
Contributed by Joel Sherrill (joel@OARcorp.com).
 
This file is part of GCC.
 
GCC is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3, or (at your option)
any later version.
 
GCC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
 
You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING3. If not see
<http://www.gnu.org/licenses/>. */
 
/* Target OS preprocessor built-ins. */
#define TARGET_OS_CPP_BUILTINS() \
do \
{ \
builtin_define_std ("h8300"); \
builtin_define ("__rtems__"); \
builtin_assert ("system=rtems"); \
} \
while (0)
/fixunssfsi.c
0,0 → 1,46
/* More subroutines needed by GCC output code on some machines. */
/* Compile this one with gcc. */
/* Copyright (C) 1989, 1992, 2001, 2002, 2003, 2004
Free Software Foundation, Inc.
 
This file is part of GCC.
 
GCC is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
 
In addition to the permissions in the GNU General Public License, the
Free Software Foundation gives you unlimited permission to link the
compiled version of this file into combinations with other programs,
and to distribute those combinations without any restriction coming
from the use of this file. (The General Public License restrictions
do apply in other respects; for example, they cover modification of
the file, and distribution when not linked into a combine
executable.)
 
GCC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
 
You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING. If not, write to
the Free Software Foundation, 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA. */
 
/* The libgcc2.c implementation gets confused by our type setup and creates
a directly recursive call, so we do our own implementation. For
the H8/300, that's in lib1funcs.asm, for H8/300H and H8S, it's here. */
 
#ifndef __H8300__
long __fixunssfsi (float a);
 
long
__fixunssfsi (float a)
{
if (a >= (float) 32768L)
return (long) (a - 32768L) + 32768L;
return (long) a;
}
#endif
/predicates.md
0,0 → 1,482
;; Predicate definitions for Renesas H8/300.
;; Copyright (C) 2005, 2007 Free Software Foundation, Inc.
;;
;; This file is part of GCC.
;;
;; GCC is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation; either version 3, or (at your option)
;; any later version.
;;
;; GCC is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;;
;; You should have received a copy of the GNU General Public License
;; along with GCC; see the file COPYING3. If not see
;; <http://www.gnu.org/licenses/>.
 
;; Return true if OP is a valid source operand for an integer move
;; instruction.
 
(define_predicate "general_operand_src"
(match_code "const_int,const_double,const,symbol_ref,label_ref,subreg,reg,mem")
{
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.
 
(define_predicate "general_operand_dst"
(match_code "subreg,reg,mem")
{
if (GET_MODE (op) == mode
&& GET_CODE (op) == MEM
&& GET_CODE (XEXP (op, 0)) == PRE_DEC)
return 1;
return general_operand (op, mode);
})
 
;; Likewise the second operand.
 
(define_predicate "h8300_src_operand"
(match_code "const_int,const_double,const,symbol_ref,label_ref,subreg,reg,mem")
{
if (TARGET_H8300SX)
return general_operand (op, mode);
return nonmemory_operand (op, mode);
})
 
;; Return true if OP is a suitable first operand for a general
;; arithmetic insn such as "add".
 
(define_predicate "h8300_dst_operand"
(match_code "subreg,reg,mem")
{
if (TARGET_H8300SX)
return nonimmediate_operand (op, mode);
return register_operand (op, mode);
})
 
;; Check that an operand is either a register or an unsigned 4-bit
;; constant.
 
(define_predicate "nibble_operand"
(match_code "const_int")
{
return (GET_CODE (op) == CONST_INT && TARGET_H8300SX
&& INTVAL (op) >= 0 && INTVAL (op) <= 15);
})
 
;; Check that an operand is either a register or an unsigned 4-bit
;; constant.
 
(define_predicate "reg_or_nibble_operand"
(match_code "const_int,subreg,reg")
{
return (nibble_operand (op, mode) || register_operand (op, mode));
})
 
;; Return true if X is a shift operation of type H8SX_SHIFT_UNARY.
 
(define_predicate "h8sx_unary_shift_operator"
(match_code "ashiftrt,lshiftrt,ashift,rotate")
{
return (BINARY_P (op) && NON_COMMUTATIVE_P (op)
&& (h8sx_classify_shift (GET_MODE (op), GET_CODE (op), XEXP (op, 1))
== H8SX_SHIFT_UNARY));
})
 
;; Likewise H8SX_SHIFT_BINARY.
 
(define_predicate "h8sx_binary_shift_operator"
(match_code "ashiftrt,lshiftrt,ashift")
{
return (BINARY_P (op) && NON_COMMUTATIVE_P (op)
&& (h8sx_classify_shift (GET_MODE (op), GET_CODE (op), XEXP (op, 1))
== H8SX_SHIFT_BINARY));
})
 
;; Return true if OP is a binary operator in which it would be safe to
;; replace register operands with memory operands.
 
(define_predicate "h8sx_binary_memory_operator"
(match_code "plus,minus,and,ior,xor,ashift,ashiftrt,lshiftrt,rotate")
{
if (!TARGET_H8300SX)
return false;
 
if (GET_MODE (op) != QImode
&& GET_MODE (op) != HImode
&& GET_MODE (op) != SImode)
return false;
 
switch (GET_CODE (op))
{
case PLUS:
case MINUS:
case AND:
case IOR:
case XOR:
return true;
 
default:
return h8sx_unary_shift_operator (op, mode);
}
})
 
;; Like h8sx_binary_memory_operator, but applies to unary operators.
 
(define_predicate "h8sx_unary_memory_operator"
(match_code "neg,not")
{
if (!TARGET_H8300SX)
return false;
 
if (GET_MODE (op) != QImode
&& GET_MODE (op) != HImode
&& GET_MODE (op) != SImode)
return false;
 
switch (GET_CODE (op))
{
case NEG:
case NOT:
return true;
 
default:
return false;
}
})
 
;; Return true if X is an ldm.l pattern. X is known to be parallel.
 
(define_predicate "h8300_ldm_parallel"
(match_code "parallel")
{
return h8300_ldm_stm_parallel (XVEC (op, 0), 1, 0);
})
 
;; Likewise stm.l.
 
(define_predicate "h8300_stm_parallel"
(match_code "parallel")
{
return h8300_ldm_stm_parallel (XVEC (op, 0), 0, 0);
})
 
;; Likewise rts/l and rte/l. Note that the .md pattern will check for
;; the return so there's no need to do that here.
 
(define_predicate "h8300_return_parallel"
(match_code "parallel")
{
return h8300_ldm_stm_parallel (XVEC (op, 0), 1, 1);
})
 
;; Return true if OP is a constant that contains only one 1 in its
;; binary representation.
 
(define_predicate "single_one_operand"
(match_code "const_int")
{
if (GET_CODE (op) == CONST_INT)
{
/* We really need to do this masking because 0x80 in QImode is
represented as -128 for example. */
if (exact_log2 (INTVAL (op) & 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.
 
(define_predicate "single_zero_operand"
(match_code "const_int")
{
if (GET_CODE (op) == CONST_INT)
{
/* We really need to do this masking because 0x80 in QImode is
represented as -128 for example. */
if (exact_log2 (~INTVAL (op) & GET_MODE_MASK (mode)) >= 0)
return 1;
}
 
return 0;
})
 
;; Return true if OP is a valid call operand.
 
(define_predicate "call_insn_operand"
(match_code "mem")
{
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 true if OP is a valid call operand, and OP represents an
;; operand for a small call (4 bytes instead of 6 bytes).
 
(define_predicate "small_call_insn_operand"
(match_code "mem")
{
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.
 
(define_predicate "jump_address_operand"
(match_code "reg,mem")
{
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;
})
 
;; 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.
 
(define_predicate "two_insn_adds_subs_operand"
(match_code "const_int")
{
if (TARGET_H8300SX)
return 0;
 
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;
})
 
;; Recognize valid operands for bit-field instructions.
 
(define_predicate "bit_operand"
(match_code "reg,subreg,mem")
{
/* We can accept any nonimmediate operand, except that MEM operands must
be limited to those that use addresses valid for the 'U' constraint. */
if (!nonimmediate_operand (op, mode))
return 0;
 
/* H8SX accepts pretty much anything here. */
if (TARGET_H8300SX)
return 1;
 
/* Accept any mem during RTL generation. Otherwise, the code that does
insv and extzv will think that we cannot 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
&& OK_FOR_U (op));
})
 
;; Return nonzero if OP is a MEM suitable for bit manipulation insns.
 
(define_predicate "bit_memory_operand"
(match_code "mem")
{
return (GET_CODE (op) == MEM
&& OK_FOR_U (op));
})
 
;; Return nonzero if X is a stack pointer.
 
(define_predicate "stack_pointer_operand"
(match_code "reg")
{
return op == stack_pointer_rtx;
})
 
;; Return nonzero if X is a constant whose absolute value is greater
;; than 2.
 
(define_predicate "const_int_gt_2_operand"
(match_code "const_int")
{
return (GET_CODE (op) == CONST_INT
&& abs (INTVAL (op)) > 2);
})
 
;; Return nonzero if X is a constant whose absolute value is no
;; smaller than 8.
 
(define_predicate "const_int_ge_8_operand"
(match_code "const_int")
{
return (GET_CODE (op) == CONST_INT
&& abs (INTVAL (op)) >= 8);
})
 
;; Return nonzero if X is a constant expressible in QImode.
 
(define_predicate "const_int_qi_operand"
(match_code "const_int")
{
return (GET_CODE (op) == CONST_INT
&& (INTVAL (op) & 0xff) == INTVAL (op));
})
 
;; Return nonzero if X is a constant expressible in HImode.
 
(define_predicate "const_int_hi_operand"
(match_code "const_int")
{
return (GET_CODE (op) == CONST_INT
&& (INTVAL (op) & 0xffff) == INTVAL (op));
})
 
;; Return nonzero if X is a constant suitable for inc/dec.
 
(define_predicate "incdec_operand"
(match_code "const_int")
{
return (GET_CODE (op) == CONST_INT
&& (CONST_OK_FOR_M (INTVAL (op))
|| CONST_OK_FOR_O (INTVAL (op))));
})
 
;; Recognize valid operators for bit instructions.
 
(define_predicate "bit_operator"
(match_code "xor,and,ior")
{
enum rtx_code code = GET_CODE (op);
 
return (code == XOR
|| code == AND
|| code == IOR);
})
 
;; Return nonzero if OP is a shift operator.
 
(define_predicate "nshift_operator"
(match_code "ashiftrt,lshiftrt,ashift")
{
switch (GET_CODE (op))
{
case ASHIFTRT:
case LSHIFTRT:
case ASHIFT:
return 1;
 
default:
return 0;
}
})
 
;; Return nonzero if X is either EQ or NE.
 
(define_predicate "eqne_operator"
(match_code "eq,ne")
{
enum rtx_code code = GET_CODE (op);
 
return (code == EQ || code == NE);
})
 
;; Return nonzero if X is either GT or LE.
 
(define_predicate "gtle_operator"
(match_code "gt,le,gtu,leu")
{
enum rtx_code code = GET_CODE (op);
 
return (code == GT || code == LE);
})
 
;; Return nonzero if X is either GTU or LEU.
 
(define_predicate "gtuleu_operator"
(match_code "gtu,leu")
{
enum rtx_code code = GET_CODE (op);
 
return (code == GTU || code == LEU);
})
 
;; Return nonzero if X is either IOR or XOR.
 
(define_predicate "iorxor_operator"
(match_code "ior,xor")
{
enum rtx_code code = GET_CODE (op);
 
return (code == IOR || code == XOR);
})
/h8300.c
0,0 → 1,5751
/* 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, 2007 Free Software Foundation, Inc.
Contributed by Steve Chamberlain (sac@cygnus.com),
Jim Wilson (wilson@cygnus.com), and Doug Evans (dje@cygnus.com).
 
This file is part of GCC.
 
GCC is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3, or (at your option)
any later version.
 
GCC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
 
You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING3. If not see
<http://www.gnu.org/licenses/>. */
 
#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "tm.h"
#include "rtl.h"
#include "tree.h"
#include "regs.h"
#include "hard-reg-set.h"
#include "real.h"
#include "insn-config.h"
#include "conditions.h"
#include "output.h"
#include "insn-attr.h"
#include "flags.h"
#include "recog.h"
#include "expr.h"
#include "function.h"
#include "optabs.h"
#include "toplev.h"
#include "c-pragma.h"
#include "tm_p.h"
#include "ggc.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 (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, HOST_WIDE_INT);
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 *);
#ifndef OBJECT_FORMAT_ELF
static void h8300_asm_named_section (const char *, unsigned int, tree);
#endif
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;
 
/* True if a #pragma interrupt has been seen for the current function. */
static int pragma_interrupt;
 
/* True if a #pragma saveall has been seen for the current function. */
static int pragma_saveall;
 
static const char *const names_big[] =
{ "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7" };
 
static const char *const names_extended[] =
{ "er0", "er1", "er2", "er3", "er4", "er5", "er6", "er7" };
 
static const char *const names_upper_extended[] =
{ "e0", "e1", "e2", "e3", "e4", "e5", "e6", "e7" };
 
/* Points to one of the above. */
/* ??? The above could be put in an array indexed by CPU_TYPE. */
const char * const *h8_reg_names;
 
/* Various operations needed by the following, indexed by CPU_TYPE. */
 
const char *h8_push_op, *h8_pop_op, *h8_mov_op;
 
/* Value of MOVE_RATIO. */
int h8300_move_ratio;
/* See below where shifts are handled for explanation of this enum. */
 
enum shift_alg
{
SHIFT_INLINE,
SHIFT_ROT_AND,
SHIFT_SPECIAL,
SHIFT_LOOP
};
 
/* Symbols of the various shifts which can be used as indices. */
 
enum shift_type
{
SHIFT_ASHIFT, SHIFT_LSHIFTRT, SHIFT_ASHIFTRT
};
 
/* Macros to keep the shift algorithm tables small. */
#define INL SHIFT_INLINE
#define ROT SHIFT_ROT_AND
#define LOP SHIFT_LOOP
#define SPC SHIFT_SPECIAL
 
/* The shift algorithms for each machine, mode, shift type, and shift
count are defined below. The three tables below correspond to
QImode, HImode, and SImode, respectively. Each table is organized
by, in the order of indices, machine, shift type, and shift count. */
 
static enum shift_alg shift_alg_qi[3][3][8] = {
{
/* TARGET_H8300 */
/* 0 1 2 3 4 5 6 7 */
{ INL, INL, INL, INL, INL, ROT, ROT, ROT }, /* SHIFT_ASHIFT */
{ INL, INL, INL, INL, INL, ROT, ROT, ROT }, /* SHIFT_LSHIFTRT */
{ INL, INL, INL, INL, INL, LOP, LOP, SPC } /* SHIFT_ASHIFTRT */
},
{
/* TARGET_H8300H */
/* 0 1 2 3 4 5 6 7 */
{ INL, INL, INL, INL, INL, ROT, ROT, ROT }, /* SHIFT_ASHIFT */
{ INL, INL, INL, INL, INL, ROT, ROT, ROT }, /* SHIFT_LSHIFTRT */
{ INL, INL, INL, INL, INL, LOP, LOP, SPC } /* SHIFT_ASHIFTRT */
},
{
/* TARGET_H8300S */
/* 0 1 2 3 4 5 6 7 */
{ INL, INL, INL, INL, INL, INL, ROT, ROT }, /* SHIFT_ASHIFT */
{ INL, INL, INL, INL, INL, INL, ROT, ROT }, /* SHIFT_LSHIFTRT */
{ INL, INL, INL, INL, INL, INL, INL, SPC } /* SHIFT_ASHIFTRT */
}
};
 
static enum shift_alg shift_alg_hi[3][3][16] = {
{
/* TARGET_H8300 */
/* 0 1 2 3 4 5 6 7 */
/* 8 9 10 11 12 13 14 15 */
{ INL, INL, INL, INL, INL, INL, INL, SPC,
SPC, SPC, SPC, SPC, SPC, SPC, SPC, SPC }, /* SHIFT_ASHIFT */
{ INL, INL, INL, INL, INL, LOP, LOP, SPC,
SPC, SPC, SPC, SPC, SPC, SPC, SPC, SPC }, /* SHIFT_LSHIFTRT */
{ INL, INL, INL, INL, INL, LOP, LOP, SPC,
SPC, SPC, SPC, SPC, SPC, SPC, SPC, SPC }, /* SHIFT_ASHIFTRT */
},
{
/* TARGET_H8300H */
/* 0 1 2 3 4 5 6 7 */
/* 8 9 10 11 12 13 14 15 */
{ INL, INL, INL, INL, INL, INL, INL, SPC,
SPC, SPC, SPC, SPC, SPC, ROT, ROT, ROT }, /* SHIFT_ASHIFT */
{ INL, INL, INL, INL, INL, INL, INL, SPC,
SPC, SPC, SPC, SPC, SPC, ROT, ROT, ROT }, /* SHIFT_LSHIFTRT */
{ INL, INL, INL, INL, INL, INL, INL, SPC,
SPC, SPC, SPC, SPC, SPC, SPC, SPC, SPC }, /* SHIFT_ASHIFTRT */
},
{
/* TARGET_H8300S */
/* 0 1 2 3 4 5 6 7 */
/* 8 9 10 11 12 13 14 15 */
{ INL, INL, INL, INL, INL, INL, INL, INL,
SPC, SPC, SPC, SPC, SPC, ROT, ROT, ROT }, /* SHIFT_ASHIFT */
{ INL, INL, INL, INL, INL, INL, INL, INL,
SPC, SPC, SPC, SPC, SPC, ROT, ROT, ROT }, /* SHIFT_LSHIFTRT */
{ INL, INL, INL, INL, INL, INL, INL, INL,
SPC, SPC, SPC, SPC, SPC, SPC, SPC, SPC }, /* SHIFT_ASHIFTRT */
}
};
 
static enum shift_alg shift_alg_si[3][3][32] = {
{
/* TARGET_H8300 */
/* 0 1 2 3 4 5 6 7 */
/* 8 9 10 11 12 13 14 15 */
/* 16 17 18 19 20 21 22 23 */
/* 24 25 26 27 28 29 30 31 */
{ INL, INL, INL, LOP, LOP, LOP, LOP, LOP,
SPC, LOP, LOP, LOP, LOP, LOP, LOP, LOP,
SPC, SPC, SPC, SPC, SPC, LOP, LOP, LOP,
SPC, SPC, SPC, SPC, LOP, LOP, LOP, SPC }, /* SHIFT_ASHIFT */
{ INL, INL, INL, LOP, LOP, LOP, LOP, LOP,
SPC, SPC, LOP, LOP, LOP, LOP, LOP, SPC,
SPC, SPC, SPC, LOP, LOP, LOP, LOP, LOP,
SPC, SPC, SPC, SPC, SPC, LOP, LOP, SPC }, /* SHIFT_LSHIFTRT */
{ INL, INL, INL, LOP, LOP, LOP, LOP, LOP,
SPC, LOP, LOP, LOP, LOP, LOP, LOP, SPC,
SPC, SPC, LOP, LOP, LOP, LOP, LOP, LOP,
SPC, SPC, SPC, LOP, LOP, LOP, LOP, SPC }, /* SHIFT_ASHIFTRT */
},
{
/* TARGET_H8300H */
/* 0 1 2 3 4 5 6 7 */
/* 8 9 10 11 12 13 14 15 */
/* 16 17 18 19 20 21 22 23 */
/* 24 25 26 27 28 29 30 31 */
{ INL, INL, INL, INL, INL, LOP, LOP, LOP,
SPC, LOP, LOP, LOP, LOP, LOP, LOP, SPC,
SPC, SPC, SPC, SPC, LOP, LOP, LOP, LOP,
SPC, LOP, LOP, LOP, SPC, SPC, SPC, SPC }, /* SHIFT_ASHIFT */
{ INL, INL, INL, INL, INL, LOP, LOP, LOP,
SPC, LOP, LOP, LOP, LOP, LOP, LOP, SPC,
SPC, SPC, SPC, SPC, LOP, LOP, LOP, LOP,
SPC, LOP, LOP, LOP, SPC, SPC, SPC, SPC }, /* SHIFT_LSHIFTRT */
{ INL, INL, INL, INL, INL, LOP, LOP, LOP,
SPC, LOP, LOP, LOP, LOP, LOP, LOP, LOP,
SPC, SPC, SPC, SPC, LOP, LOP, LOP, LOP,
SPC, LOP, LOP, LOP, LOP, LOP, LOP, SPC }, /* SHIFT_ASHIFTRT */
},
{
/* TARGET_H8300S */
/* 0 1 2 3 4 5 6 7 */
/* 8 9 10 11 12 13 14 15 */
/* 16 17 18 19 20 21 22 23 */
/* 24 25 26 27 28 29 30 31 */
{ INL, INL, INL, INL, INL, INL, INL, INL,
INL, INL, INL, LOP, LOP, LOP, LOP, SPC,
SPC, SPC, SPC, SPC, SPC, SPC, LOP, LOP,
SPC, SPC, LOP, LOP, SPC, SPC, SPC, SPC }, /* SHIFT_ASHIFT */
{ INL, INL, INL, INL, INL, INL, INL, INL,
INL, INL, INL, LOP, LOP, LOP, LOP, SPC,
SPC, SPC, SPC, SPC, SPC, SPC, LOP, LOP,
SPC, SPC, LOP, LOP, SPC, SPC, SPC, SPC }, /* SHIFT_LSHIFTRT */
{ INL, INL, INL, INL, INL, INL, INL, INL,
INL, INL, INL, LOP, LOP, LOP, LOP, LOP,
SPC, SPC, SPC, SPC, SPC, SPC, LOP, LOP,
SPC, SPC, LOP, LOP, LOP, LOP, LOP, SPC }, /* SHIFT_ASHIFTRT */
}
};
 
#undef INL
#undef ROT
#undef LOP
#undef SPC
 
enum h8_cpu
{
H8_300,
H8_300H,
H8_S
};
 
/* Initialize various cpu specific globals at start up. */
 
void
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" };
static const char *const h8_mov_ops[2] = { "mov.w", "mov.l" };
 
if (TARGET_H8300)
{
cpu_type = (int) CPU_H8300;
h8_reg_names = names_big;
}
else
{
/* For this we treat the H8/300H and H8S the same. */
cpu_type = (int) CPU_H8300H;
h8_reg_names = names_extended;
}
h8_push_op = h8_push_ops[cpu_type];
h8_pop_op = h8_pop_ops[cpu_type];
h8_mov_op = h8_mov_ops[cpu_type];
 
if (!TARGET_H8300S && TARGET_MAC)
{
error ("-ms2600 is used without -ms");
target_flags |= MASK_H8300S_1;
}
 
if (TARGET_H8300 && TARGET_NORMAL_MODE)
{
error ("-mn is used without -mh or -ms");
target_flags ^= MASK_NORMAL_MODE;
}
 
/* Some of the shifts are optimized for speed by default.
See http://gcc.gnu.org/ml/gcc-patches/2002-07/msg01858.html
If optimizing for size, change shift_alg for those shift to
SHIFT_LOOP. */
if (optimize_size)
{
/* H8/300 */
shift_alg_hi[H8_300][SHIFT_ASHIFT][5] = SHIFT_LOOP;
shift_alg_hi[H8_300][SHIFT_ASHIFT][6] = SHIFT_LOOP;
shift_alg_hi[H8_300][SHIFT_ASHIFT][13] = SHIFT_LOOP;
shift_alg_hi[H8_300][SHIFT_ASHIFT][14] = SHIFT_LOOP;
 
shift_alg_hi[H8_300][SHIFT_LSHIFTRT][13] = SHIFT_LOOP;
shift_alg_hi[H8_300][SHIFT_LSHIFTRT][14] = SHIFT_LOOP;
 
shift_alg_hi[H8_300][SHIFT_ASHIFTRT][13] = SHIFT_LOOP;
shift_alg_hi[H8_300][SHIFT_ASHIFTRT][14] = SHIFT_LOOP;
 
/* H8/300H */
shift_alg_hi[H8_300H][SHIFT_ASHIFT][5] = SHIFT_LOOP;
shift_alg_hi[H8_300H][SHIFT_ASHIFT][6] = SHIFT_LOOP;
 
shift_alg_hi[H8_300H][SHIFT_LSHIFTRT][5] = SHIFT_LOOP;
shift_alg_hi[H8_300H][SHIFT_LSHIFTRT][6] = SHIFT_LOOP;
 
shift_alg_hi[H8_300H][SHIFT_ASHIFTRT][5] = SHIFT_LOOP;
shift_alg_hi[H8_300H][SHIFT_ASHIFTRT][6] = SHIFT_LOOP;
shift_alg_hi[H8_300H][SHIFT_ASHIFTRT][13] = SHIFT_LOOP;
shift_alg_hi[H8_300H][SHIFT_ASHIFTRT][14] = SHIFT_LOOP;
 
/* H8S */
shift_alg_hi[H8_S][SHIFT_ASHIFTRT][14] = SHIFT_LOOP;
}
 
/* 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 (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"
};
 
gcc_assert (REG_P (x));
 
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 < SP_REG \
/* No need to save registers if this function will not return. */ \
&& ! TREE_THIS_VOLATILE (current_function_decl) \
&& (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 == 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]) \
/* Save call clobbered registers in non-leaf interrupt \
handlers. */ \
|| (h8300_current_function_interrupt_function_p () \
&& call_used_regs[regno] \
&& !current_function_is_leaf)))
 
/* Output assembly language to FILE for the operation OP with operand size
SIZE to adjust the stack pointer. */
 
static void
h8300_emit_stack_adjustment (int sign, HOST_WIDE_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 ()
&& !(cfun->static_chain_decl != NULL && sign < 0))
{
rtx r3 = gen_rtx_REG (Pmode, 3);
emit_insn (gen_movhi (r3, GEN_INT (sign * size)));
emit_insn (gen_addhi3 (stack_pointer_rtx,
stack_pointer_rtx, r3));
}
else
{
/* The stack adjustment made here is further optimized by the
splitter. In case of H8/300, the splitter always splits the
addition emitted here to make the adjustment
interrupt-safe. */
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 HOST_WIDE_INT
round_frame_size (HOST_WIDE_INT size)
{
return ((size + STACK_BOUNDARY / BITS_PER_UNIT - 1)
& -STACK_BOUNDARY / BITS_PER_UNIT);
}
 
/* Compute which registers to push/pop.
Return a bit vector of registers. */
 
static unsigned int
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 <= 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 << HARD_FRAME_POINTER_REGNUM);
 
return saved_regs;
}
 
/* Emit an insn to push register RN. */
 
static void
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_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 (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_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:
 
<args>
PC
FP <- fp
<locals>
<saved registers> <- sp
 
This is what the stack looks like after the prolog of
a function which doesn't have a frame:
 
<args>
PC
<locals>
<saved registers> <- sp
*/
 
/* Generate RTL code for the function prologue. */
 
void
h8300_expand_prologue (void)
{
int regno;
int saved_regs;
int n_regs;
 
/* If the current function has the OS_Task attribute set, then
we have a naked prologue. */
if (h8300_os_task_function_p (current_function_decl))
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. */
emit_insn (gen_monitor_prologue ());
 
if (frame_pointer_needed)
{
/* Push fp. */
push (HARD_FRAME_POINTER_REGNUM);
emit_move_insn (hard_frame_pointer_rtx, stack_pointer_rtx);
}
 
/* Push the rest of the registers in ascending order. */
saved_regs = compute_saved_regs ();
for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno += n_regs)
{
n_regs = 1;
if (saved_regs & (1 << regno))
{
if (TARGET_H8300S)
{
/* See how many registers we can push at the same time. */
if ((!TARGET_H8300SX || (regno & 3) == 0)
&& ((saved_regs >> regno) & 0x0f) == 0x0f)
n_regs = 4;
 
else if ((!TARGET_H8300SX || (regno & 3) == 0)
&& ((saved_regs >> regno) & 0x07) == 0x07)
n_regs = 3;
 
else if ((!TARGET_H8300SX || (regno & 1) == 0)
&& ((saved_regs >> regno) & 0x03) == 0x03)
n_regs = 2;
}
 
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 (void)
{
return (reload_completed
&& !frame_pointer_needed
&& get_frame_size () == 0
&& compute_saved_regs () == 0);
}
 
/* Generate RTL code for the function epilogue. */
 
void
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)
{
n_regs = 1;
if (saved_regs & (1 << regno))
{
if (TARGET_H8300S)
{
/* See how many registers we can pop at the same time. */
if ((TARGET_H8300SX || (regno & 3) == 3)
&& ((saved_regs << 3 >> regno) & 0x0f) == 0x0f)
n_regs = 4;
 
else if ((TARGET_H8300SX || (regno & 3) == 2)
&& ((saved_regs << 2 >> regno) & 0x07) == 0x07)
n_regs = 3;
 
else if ((TARGET_H8300SX || (regno & 1) == 1)
&& ((saved_regs << 1 >> regno) & 0x03) == 0x03)
n_regs = 2;
}
 
/* 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);
}
}
 
/* Pop frame pointer if we had one. */
if (frame_pointer_needed)
{
if (TARGET_H8300SX)
returned_p = true;
h8300_push_pop (HARD_FRAME_POINTER_REGNUM, 1, 1, returned_p);
}
 
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 (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. */
 
static void
h8300_file_start (void)
{
default_file_start ();
 
if (TARGET_H8300H)
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)
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 (void)
{
fputs ("\t.end\n", asm_out_file);
}
/* 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 (enum machine_mode mode, rtx *operands)
{
HOST_WIDE_INT val = INTVAL (operands[1]);
rtx reg = operands[0];
HOST_WIDE_INT sign = 1;
HOST_WIDE_INT amount;
rtx (*gen_add) (rtx, rtx, rtx);
 
/* Force VAL to be positive so that we do not have to consider the
sign. */
if (val < 0)
{
val = -val;
sign = -1;
}
 
switch (mode)
{
case HImode:
gen_add = gen_addhi3;
break;
 
case SImode:
gen_add = gen_addsi3;
break;
 
default:
gcc_unreachable ();
}
 
/* Try different amounts in descending order. */
for (amount = (TARGET_H8300H || TARGET_H8300S) ? 4 : 2;
amount > 0;
amount /= 2)
{
for (; val >= amount; val -= amount)
emit_insn (gen_add (reg, reg, GEN_INT (sign * amount)));
}
 
return;
}
 
/* Handle machine specific pragmas for compatibility with existing
compilers for the H8/300.
 
pragma saveall generates prologue/epilogue code which saves and
restores all the registers on function entry.
 
pragma interrupt saves and restores all registers, and exits with
an rte instruction rather than an rts. A pointer to a function
with this attribute may be safely used in an interrupt vector. */
 
void
h8300_pr_interrupt (struct cpp_reader *pfile ATTRIBUTE_UNUSED)
{
pragma_interrupt = 1;
}
 
void
h8300_pr_saveall (struct cpp_reader *pfile ATTRIBUTE_UNUSED)
{
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. */
 
rtx
function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode,
tree type, int named)
{
static const char *const hand_list[] = {
"__main",
"__cmpsi2",
"__divhi3",
"__modhi3",
"__udivhi3",
"__umodhi3",
"__divsi3",
"__modsi3",
"__udivsi3",
"__umodsi3",
"__mulhi3",
"__mulsi3",
"__reg_memcpy",
"__reg_memset",
"__ucmpsi2",
0,
};
 
rtx result = NULL_RTX;
const char *fname;
int regpass = 0;
 
/* Never pass unnamed arguments in registers. */
if (!named)
return NULL_RTX;
 
/* Pass 3 regs worth of data in regs when user asked on the command line. */
if (TARGET_QUICKCALL)
regpass = 3;
 
/* If calling hand written assembler, use 4 regs of args. */
if (cum->libcall)
{
const char * const *p;
 
fname = XSTR (cum->libcall, 0);
 
/* See if this libcall is one of the hand coded ones. */
for (p = hand_list; *p && strcmp (*p, fname) != 0; p++)
;
 
if (*p)
regpass = 4;
}
 
if (regpass)
{
int size;
 
if (mode == BLKmode)
size = int_size_in_bytes (type);
else
size = GET_MODE_SIZE (mode);
 
if (size + cum->nbytes <= regpass * UNITS_PER_WORD
&& cum->nbytes / UNITS_PER_WORD <= 3)
result = gen_rtx_REG (mode, cum->nbytes / UNITS_PER_WORD);
}
 
return result;
}
/* Compute the cost of an and insn. */
 
static int
h8300_and_costs (rtx x)
{
rtx operands[4];
 
if (GET_MODE (x) == QImode)
return 1;
 
if (GET_MODE (x) != HImode
&& GET_MODE (x) != SImode)
return 100;
 
operands[0] = NULL;
operands[1] = 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 (rtx x)
{
rtx operands[4];
 
if (GET_MODE (x) != QImode
&& GET_MODE (x) != HImode
&& GET_MODE (x) != SImode)
return 100;
 
operands[0] = NULL;
operands[1] = NULL;
operands[2] = XEXP (x, 1);
operands[3] = x;
return compute_a_shift_length (NULL, operands) / 2;
}
 
/* Worker function for TARGET_RTX_COSTS. */
 
static bool
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;
 
/* We say that MOD and DIV are so expensive because otherwise we'll
generate some really horrible code for division of a power of two. */
case MOD:
case DIV:
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:
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;
 
case ROTATE:
case ROTATERT:
if (GET_MODE (x) == HImode)
*total = 2;
else
*total = 8;
return true;
 
default:
*total = COSTS_N_INSNS (1);
return false;
}
}
/* Documentation for the machine specific operand escapes:
 
'E' like s but negative.
'F' like t but negative.
'G' constant just the negative
'R' print operand as a byte:8 address if appropriate, else fall back to
'X' handling.
'S' print operand as a long word
'T' print operand as a word
'V' find the set bit, and print its number.
'W' find the clear bit, and print its number.
'X' print operand as a byte
'Y' print either l or h depending on whether last 'Z' operand < 8 or >= 8.
If this operand isn't a register, fall back to 'R' handling.
'Z' print int & 7.
'c' print the opcode corresponding to rtl
'e' first word of 32 bit value - if reg, then least reg. if mem
then least. if const then most sig word
'f' second word of 32 bit value - if reg, then biggest reg. if mem
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
'x' print as 2nd byte of 32 bit value
'y' print as 3rd byte of 32 bit value
'z' print as msb of 32 bit value
*/
 
/* Return assembly language string which identifies a comparison type. */
 
static const char *
cond_string (enum rtx_code code)
{
switch (code)
{
case NE:
return "ne";
case EQ:
return "eq";
case GE:
return "ge";
case GT:
return "gt";
case LE:
return "le";
case LT:
return "lt";
case GEU:
return "hs";
case GTU:
return "hi";
case LEU:
return "ls";
case LTU:
return "lo";
default:
gcc_unreachable ();
}
}
 
/* Print operand X using operand code CODE to assembly language output file
FILE. */
 
void
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 'E':
switch (GET_CODE (x))
{
case REG:
fprintf (file, "%sl", names_big[REGNO (x)]);
break;
case CONST_INT:
fprintf (file, "#%ld", (-INTVAL (x)) & 0xff);
break;
default:
gcc_unreachable ();
}
break;
case 'F':
switch (GET_CODE (x))
{
case REG:
fprintf (file, "%sh", names_big[REGNO (x)]);
break;
case CONST_INT:
fprintf (file, "#%ld", ((-INTVAL (x)) & 0xff00) >> 8);
break;
default:
gcc_unreachable ();
}
break;
case 'G':
gcc_assert (GET_CODE (x) == CONST_INT);
fprintf (file, "#%ld", 0xff & (-INTVAL (x)));
break;
case 'S':
if (GET_CODE (x) == REG)
fprintf (file, "%s", names_extended[REGNO (x)]);
else
goto def;
break;
case 'T':
if (GET_CODE (x) == REG)
fprintf (file, "%s", names_big[REGNO (x)]);
else
goto def;
break;
case 'V':
bitint = exact_log2 (INTVAL (x) & 0xff);
gcc_assert (bitint >= 0);
fprintf (file, "#%d", bitint);
break;
case 'W':
bitint = exact_log2 ((~INTVAL (x)) & 0xff);
gcc_assert (bitint >= 0);
fprintf (file, "#%d", bitint);
break;
case 'R':
case 'X':
if (GET_CODE (x) == REG)
fprintf (file, "%s", byte_reg (x, 0));
else
goto def;
break;
case 'Y':
gcc_assert (bitint >= 0);
if (GET_CODE (x) == REG)
fprintf (file, "%s%c", names_big[REGNO (x)], bitint > 7 ? 'h' : 'l');
else
print_operand (file, x, 'R');
bitint = -1;
break;
case 'Z':
bitint = INTVAL (x);
fprintf (file, "#%d", bitint & 7);
break;
case 'c':
switch (GET_CODE (x))
{
case IOR:
fprintf (file, "or");
break;
case XOR:
fprintf (file, "xor");
break;
case AND:
fprintf (file, "and");
break;
default:
break;
}
break;
case 'e':
switch (GET_CODE (x))
{
case REG:
if (TARGET_H8300)
fprintf (file, "%s", names_big[REGNO (x)]);
else
fprintf (file, "%s", names_upper_extended[REGNO (x)]);
break;
case MEM:
print_operand (file, x, 0);
break;
case CONST_INT:
fprintf (file, "#%ld", ((INTVAL (x) >> 16) & 0xffff));
break;
case CONST_DOUBLE:
{
long val;
REAL_VALUE_TYPE rv;
REAL_VALUE_FROM_CONST_DOUBLE (rv, x);
REAL_VALUE_TO_TARGET_SINGLE (rv, val);
fprintf (file, "#%ld", ((val >> 16) & 0xffff));
break;
}
default:
gcc_unreachable ();
break;
}
break;
case 'f':
switch (GET_CODE (x))
{
case REG:
if (TARGET_H8300)
fprintf (file, "%s", names_big[REGNO (x) + 1]);
else
fprintf (file, "%s", names_big[REGNO (x)]);
break;
case MEM:
x = adjust_address (x, HImode, 2);
print_operand (file, x, 0);
break;
case CONST_INT:
fprintf (file, "#%ld", INTVAL (x) & 0xffff);
break;
case CONST_DOUBLE:
{
long val;
REAL_VALUE_TYPE rv;
REAL_VALUE_FROM_CONST_DOUBLE (rv, x);
REAL_VALUE_TO_TARGET_SINGLE (rv, val);
fprintf (file, "#%ld", (val & 0xffff));
break;
}
default:
gcc_unreachable ();
}
break;
case 'j':
fputs (cond_string (GET_CODE (x)), file);
break;
case 'k':
fputs (cond_string (reverse_condition (GET_CODE (x))), file);
break;
case 'm':
gcc_assert (GET_CODE (x) == CONST_INT);
switch (INTVAL (x))
{
case 1:
fputs (".b", file);
break;
 
case 2:
fputs (".w", file);
break;
 
case 4:
fputs (".l", file);
break;
 
default:
gcc_unreachable ();
}
break;
case 'o':
print_operand_address (file, x);
break;
case 's':
if (GET_CODE (x) == CONST_INT)
fprintf (file, "#%ld", (INTVAL (x)) & 0xff);
else
fprintf (file, "%s", byte_reg (x, 0));
break;
case 't':
if (GET_CODE (x) == CONST_INT)
fprintf (file, "#%ld", (INTVAL (x) >> 8) & 0xff);
else
fprintf (file, "%s", byte_reg (x, 1));
break;
case 'w':
if (GET_CODE (x) == CONST_INT)
fprintf (file, "#%ld", INTVAL (x) & 0xff);
else
fprintf (file, "%s",
byte_reg (x, TARGET_H8300 ? 2 : 0));
break;
case 'x':
if (GET_CODE (x) == CONST_INT)
fprintf (file, "#%ld", (INTVAL (x) >> 8) & 0xff);
else
fprintf (file, "%s",
byte_reg (x, TARGET_H8300 ? 3 : 1));
break;
case 'y':
if (GET_CODE (x) == CONST_INT)
fprintf (file, "#%ld", (INTVAL (x) >> 16) & 0xff);
else
fprintf (file, "%s", byte_reg (x, 0));
break;
case 'z':
if (GET_CODE (x) == CONST_INT)
fprintf (file, "#%ld", (INTVAL (x) >> 24) & 0xff);
else
fprintf (file, "%s", byte_reg (x, 1));
break;
 
default:
def:
switch (GET_CODE (x))
{
case REG:
switch (GET_MODE (x))
{
case QImode:
#if 0 /* 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:
fprintf (file, "%s", names_big[REGNO (x)]);
break;
case SImode:
case SFmode:
fprintf (file, "%s", names_extended[REGNO (x)]);
break;
default:
gcc_unreachable ();
}
break;
 
case MEM:
{
rtx addr = XEXP (x, 0);
 
fprintf (file, "@");
output_address (addr);
 
/* 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;
 
case CONST_INT:
case SYMBOL_REF:
case CONST:
case LABEL_REF:
fprintf (file, "#");
print_operand_address (file, x);
break;
case CONST_DOUBLE:
{
long val;
REAL_VALUE_TYPE rv;
REAL_VALUE_FROM_CONST_DOUBLE (rv, x);
REAL_VALUE_TO_TARGET_SINGLE (rv, val);
fprintf (file, "#%ld", val);
break;
}
default:
break;
}
}
}
 
/* Output assembly language output for the address ADDR to FILE. */
 
void
print_operand_address (FILE *file, rtx addr)
{
rtx index;
int size;
 
switch (GET_CODE (addr))
{
case REG:
fprintf (file, "%s", h8_reg_names[REGNO (addr)]);
break;
 
case PRE_DEC:
fprintf (file, "-%s", h8_reg_names[REGNO (XEXP (addr, 0))]);
break;
 
case POST_INC:
fprintf (file, "%s+", h8_reg_names[REGNO (XEXP (addr, 0))]);
break;
 
case 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, "(");
 
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, ",");
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
{
/* foo+k */
print_operand_address (file, XEXP (addr, 0));
fprintf (file, "+");
print_operand_address (file, XEXP (addr, 1));
}
fprintf (file, ")");
break;
 
case CONST_INT:
{
/* Since the H8/300 only has 16 bit pointers, negative values are also
those >= 32768. This happens for example with pointer minus a
constant. We don't want to turn (char *p - 2) into
(char *p + 65534) because loop unrolling can build upon this
(IE: char *p + 131068). */
int n = INTVAL (addr);
if (TARGET_H8300)
n = (int) (short) n;
fprintf (file, "%d", n);
break;
}
 
default:
output_addr_const (file, addr);
break;
}
}
/* Output all insn addresses and their sizes into the assembly language
output file. This is helpful for debugging whether the length attributes
in the md file are correct. This is not meant to be a user selectable
option. */
 
void
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_ADDRESSES)
{
fprintf (asm_out_file, "; 0x%x %d\n", INSN_ADDRESSES (uid),
INSN_ADDRESSES (uid) - last_insn_address);
last_insn_address = INSN_ADDRESSES (uid);
}
}
 
/* Prepare for an SI sized move. */
 
int
h8300_expand_movsi (rtx operands[])
{
rtx src = operands[1];
rtx dst = operands[0];
if (!reload_in_progress && !reload_completed)
{
if (!register_operand (dst, GET_MODE (dst)))
{
rtx tmp = gen_reg_rtx (GET_MODE (dst));
emit_move_insn (tmp, src);
operands[1] = tmp;
}
}
return 0;
}
 
/* 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. */
 
int
h8300_initial_elimination_offset (int from, int to)
{
/* The number of bytes that the return address takes on the stack. */
int pc_size = POINTER_SIZE / BITS_PER_UNIT;
 
/* 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;
 
/* The number of bytes that the locals takes on the stack. */
int frame_size = round_frame_size (get_frame_size ());
 
int regno;
 
for (regno = 0; regno <= HARD_FRAME_POINTER_REGNUM; regno++)
if (WORD_REG_USED (regno))
saved_regs_size += UNITS_PER_WORD;
 
/* Adjust saved_regs_size because the above loop took the frame
pointer int account. */
saved_regs_size -= fp_size;
 
switch (to)
{
case 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:
gcc_unreachable ();
}
break;
case 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:
gcc_unreachable ();
}
break;
default:
gcc_unreachable ();
}
gcc_unreachable ();
}
 
/* Worker function for RETURN_ADDR_RTX. */
 
rtx
h8300_return_addr_rtx (int count, rtx frame)
{
rtx ret;
 
if (count == 0)
ret = gen_rtx_MEM (Pmode,
gen_rtx_REG (Pmode, RETURN_ADDRESS_POINTER_REGNUM));
else if (flag_omit_frame_pointer)
return (rtx) 0;
else
ret = gen_rtx_MEM (Pmode,
memory_address (Pmode,
plus_constant (frame, UNITS_PER_WORD)));
set_mem_alias_set (ret, get_frame_alias_set ());
return ret;
}
 
/* Update the condition code from the insn. */
 
void
notice_update_cc (rtx body, rtx insn)
{
rtx set;
 
switch (get_attr_cc (insn))
{
case CC_NONE:
/* Insn does not affect CC at all. */
break;
 
case CC_NONE_0HIT:
/* Insn does not change CC, but the 0'th operand has been changed. */
if (cc_status.value1 != 0
&& reg_overlap_mentioned_p (recog_data.operand[0], cc_status.value1))
cc_status.value1 = 0;
if (cc_status.value2 != 0
&& reg_overlap_mentioned_p (recog_data.operand[0], cc_status.value2))
cc_status.value2 = 0;
break;
 
case CC_SET_ZN:
/* Insn sets the Z,N flags of CC to recog_data.operand[0].
The V flag is unusable. The C flag may or may not be known but
that's ok because alter_cond will change tests to use EQ/NE. */
CC_STATUS_INIT;
cc_status.flags |= CC_OVERFLOW_UNUSABLE | CC_NO_CARRY;
set = single_set (insn);
cc_status.value1 = SET_SRC (set);
if (SET_DEST (set) != cc0_rtx)
cc_status.value2 = SET_DEST (set);
break;
 
case CC_SET_ZNV:
/* Insn sets the Z,N,V flags of CC to recog_data.operand[0].
The C flag may or may not be known but that's ok because
alter_cond will change tests to use EQ/NE. */
CC_STATUS_INIT;
cc_status.flags |= CC_NO_CARRY;
set = single_set (insn);
cc_status.value1 = SET_SRC (set);
if (SET_DEST (set) != cc0_rtx)
{
/* If the destination is STRICT_LOW_PART, strip off
STRICT_LOW_PART. */
if (GET_CODE (SET_DEST (set)) == STRICT_LOW_PART)
cc_status.value2 = XEXP (SET_DEST (set), 0);
else
cc_status.value2 = SET_DEST (set);
}
break;
 
case CC_COMPARE:
/* The insn is a compare instruction. */
CC_STATUS_INIT;
cc_status.value1 = SET_SRC (body);
break;
 
case CC_CLOBBER:
/* Insn doesn't leave CC in a usable state. */
CC_STATUS_INIT;
break;
}
}
/* 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:
 
@(dd,Rn)
@(dd,RnL.b)
@(dd,Rn.w)
@(dd,ERn.l)
 
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.
 
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)
{
int dummy, factor;
 
if (size == 0)
size = &dummy;
 
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;
}
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 */
};
 
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 */
};
 
static const h8300_length_table addl_length_table =
{
/* #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 */
};
 
#define logicb_length_table addb_length_table
#define logicw_length_table addw_length_table
 
static const h8300_length_table logicl_length_table =
{
/* #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 */
};
 
#define movw_length_table movb_length_table
 
static const h8300_length_table movl_length_table =
{
/* #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 the size of the given address or displacement constant. */
 
static unsigned int
h8300_constant_length (rtx constant)
{
/* 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 4;
}
 
/* 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. */
 
static unsigned int
h8300_displacement_length (rtx addr, int size)
{
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 h8300_constant_length (offset);
}
 
/* 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. */
 
static unsigned int
h8300_classify_operand (rtx op, int size, enum h8300_operand_class *class)
{
enum h8300_operand_class dummy;
 
if (class == 0)
class = &dummy;
 
if (CONSTANT_P (op))
{
*class = H8OP_IMMEDIATE;
 
/* Byte-sized immediates are stored in the opcode fields. */
if (size == 1)
return 0;
 
/* 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;
}
}
gcc_assert (register_operand (op, VOIDmode));
*class = H8OP_REGISTER;
return 0;
}
 
/* 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. */
 
static unsigned int
h8300_length_from_table (rtx op1, rtx op2, const h8300_length_table *table)
{
enum h8300_operand_class op1_class, op2_class;
unsigned int size, immediate_length;
 
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];
}
 
/* Return the length of a unary instruction such as neg or not given that
its operand is OP. */
 
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)
{
case H8OP_REGISTER:
return 2;
 
case H8OP_MEM_BASE:
return (size == 4 ? 6 : 4);
 
case H8OP_MEM_ABSOLUTE:
return operand_length + (size == 4 ? 6 : 4);
 
case H8OP_MEM_COMPLEX:
return operand_length + 6;
 
default:
gcc_unreachable ();
}
}
 
/* Likewise short immediate instructions such as add.w #xx:3,OP. */
 
static unsigned int
h8300_short_immediate_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)
{
case H8OP_REGISTER:
return 2;
 
case H8OP_MEM_BASE:
case H8OP_MEM_ABSOLUTE:
case H8OP_MEM_COMPLEX:
return 4 + operand_length;
 
default:
gcc_unreachable ();
}
}
 
/* 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;
gcc_assert (GET_CODE (op) != REG);
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:
gcc_unreachable ();
}
}
 
/* 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);
gcc_assert (set);
 
if (BINARY_P (SET_SRC (set)))
return h8300_length_from_table (XEXP (SET_SRC (set), 0),
XEXP (SET_SRC (set), 1), table);
else
{
gcc_assert (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);
}
}
 
/* 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:
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_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;
 
default:
gcc_unreachable ();
}
}
 
/* 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;
}
/* 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);
}
/* 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:
gcc_unreachable ();
}
 
/* 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:
gcc_unreachable ();
}
 
/* 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;
}
}
/* Output an addition insn. */
 
const char *
output_plussi (rtx *operands)
{
enum machine_mode mode = GET_MODE (operands[0]);
 
gcc_assert (mode == SImode);
 
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:
return "adds\t%2,%S0";
 
case 0xffffffff:
case 0xfffffffe:
case 0xfffffffc:
return "subs\t%G2,%S0";
 
case 0x00010000:
case 0x00020000:
operands[2] = GEN_INT (intval >> 16);
return "inc.w\t%2,%e0";
 
case 0xffff0000:
case 0xfffe0000:
operands[2] = GEN_INT (intval >> 16);
return "dec.w\t%G2,%e0";
}
 
/* See if we can finish with 4 bytes. */
if ((intval & 0xffff) == 0)
{
operands[2] = GEN_INT (intval >> 16);
return "add.w\t%2,%e0";
}
}
 
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 (rtx *operands)
{
enum machine_mode mode = GET_MODE (operands[0]);
 
gcc_assert (mode == SImode);
 
if (TARGET_H8300)
{
if (GET_CODE (operands[2]) == REG)
return 6;
 
if (GET_CODE (operands[2]) == CONST_INT)
{
HOST_WIDE_INT n = INTVAL (operands[2]);
 
if ((n & 0xffffff) == 0)
return 2;
if ((n & 0xffff) == 0)
return 4;
if ((n & 0xff) == 0)
return 6;
}
 
return 8;
}
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 2;
if (TARGET_H8300SX && (intval >= -7 && intval <= -1))
return 2;
 
/* See if we can finish with 2 bytes. */
 
switch ((unsigned int) intval & 0xffffffff)
{
case 0x00000001:
case 0x00000002:
case 0x00000004:
return 2;
 
case 0xffffffff:
case 0xfffffffe:
case 0xfffffffc:
return 2;
 
case 0x00010000:
case 0x00020000:
return 2;
 
case 0xffff0000:
case 0xfffe0000:
return 2;
}
 
/* See if we can finish with 4 bytes. */
if ((intval & 0xffff) == 0)
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 (rtx *operands)
{
enum machine_mode mode = GET_MODE (operands[0]);
 
gcc_assert (mode == SImode);
 
if (TARGET_H8300)
{
return CC_CLOBBER;
}
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 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)
{
case 0x00000001:
case 0x00000002:
case 0x00000004:
return CC_NONE_0HIT;
 
case 0xffffffff:
case 0xfffffffe:
case 0xfffffffc:
return CC_NONE_0HIT;
 
case 0x00010000:
case 0x00020000:
return CC_CLOBBER;
 
case 0xffff0000:
case 0xfffe0000:
return CC_CLOBBER;
}
 
/* See if we can finish with 4 bytes. */
if ((intval & 0xffff) == 0)
return CC_CLOBBER;
}
 
return CC_SET_ZN;
}
}
/* Output a logical insn. */
 
const char *
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. */
const unsigned HOST_WIDE_INT det = (code != AND) ? intval : ~intval;
/* Break up DET into pieces. */
const unsigned HOST_WIDE_INT b0 = (det >> 0) & 0xff;
const unsigned HOST_WIDE_INT b1 = (det >> 8) & 0xff;
const unsigned HOST_WIDE_INT b2 = (det >> 16) & 0xff;
const unsigned HOST_WIDE_INT b3 = (det >> 24) & 0xff;
const unsigned HOST_WIDE_INT w0 = (det >> 0) & 0xffff;
const unsigned HOST_WIDE_INT w1 = (det >> 16) & 0xffff;
int lower_half_easy_p = 0;
int upper_half_easy_p = 0;
/* The name of an insn. */
const char *opname;
char insn_buf[100];
 
switch (code)
{
case AND:
opname = "and";
break;
case IOR:
opname = "or";
break;
case XOR:
opname = "xor";
break;
default:
gcc_unreachable ();
}
 
switch (mode)
{
case HImode:
/* First, see if we can finish with one insn. */
if ((TARGET_H8300H || TARGET_H8300S)
&& b0 != 0
&& b1 != 0)
{
sprintf (insn_buf, "%s.w\t%%T2,%%T0", opname);
output_asm_insn (insn_buf, operands);
}
else
{
/* Take care of the lower byte. */
if (b0 != 0)
{
sprintf (insn_buf, "%s\t%%s2,%%s0", opname);
output_asm_insn (insn_buf, operands);
}
/* Take care of the upper byte. */
if (b1 != 0)
{
sprintf (insn_buf, "%s\t%%t2,%%t0", opname);
output_asm_insn (insn_buf, operands);
}
}
break;
case SImode:
if (TARGET_H8300H || TARGET_H8300S)
{
/* Determine if the lower half can be taken care of in no more
than two bytes. */
lower_half_easy_p = (b0 == 0
|| b1 == 0
|| (code != IOR && w0 == 0xffff));
 
/* Determine if the upper half can be taken care of in no more
than two bytes. */
upper_half_easy_p = ((code != IOR && w1 == 0xffff)
|| (code == AND && w1 == 0xff00));
}
 
/* Check if doing everything with one insn is no worse than
using multiple insns. */
if ((TARGET_H8300H || TARGET_H8300S)
&& w0 != 0 && w1 != 0
&& !(lower_half_easy_p && upper_half_easy_p)
&& !(code == IOR && w1 == 0xffff
&& (w0 & 0x8000) != 0 && lower_half_easy_p))
{
sprintf (insn_buf, "%s.l\t%%S2,%%S0", opname);
output_asm_insn (insn_buf, operands);
}
else
{
/* Take care of the lower and upper words individually. For
each word, we try different methods in the order of
 
1) the special insn (in case of AND or XOR),
2) the word-wise insn, and
3) The byte-wise insn. */
if (w0 == 0xffff
&& (TARGET_H8300 ? (code == AND) : (code != IOR)))
output_asm_insn ((code == AND)
? "sub.w\t%f0,%f0" : "not.w\t%f0",
operands);
else if ((TARGET_H8300H || TARGET_H8300S)
&& (b0 != 0)
&& (b1 != 0))
{
sprintf (insn_buf, "%s.w\t%%f2,%%f0", opname);
output_asm_insn (insn_buf, operands);
}
else
{
if (b0 != 0)
{
sprintf (insn_buf, "%s\t%%w2,%%w0", opname);
output_asm_insn (insn_buf, operands);
}
if (b1 != 0)
{
sprintf (insn_buf, "%s\t%%x2,%%x0", opname);
output_asm_insn (insn_buf, operands);
}
}
 
if ((w1 == 0xffff)
&& (TARGET_H8300 ? (code == AND) : (code != IOR)))
output_asm_insn ((code == AND)
? "sub.w\t%e0,%e0" : "not.w\t%e0",
operands);
else if ((TARGET_H8300H || TARGET_H8300S)
&& code == IOR
&& w1 == 0xffff
&& (w0 & 0x8000) != 0)
{
output_asm_insn ("exts.l\t%S0", operands);
}
else if ((TARGET_H8300H || TARGET_H8300S)
&& code == AND
&& w1 == 0xff00)
{
output_asm_insn ("extu.w\t%e0", operands);
}
else if (TARGET_H8300H || TARGET_H8300S)
{
if (w1 != 0)
{
sprintf (insn_buf, "%s.w\t%%e2,%%e0", opname);
output_asm_insn (insn_buf, operands);
}
}
else
{
if (b2 != 0)
{
sprintf (insn_buf, "%s\t%%y2,%%y0", opname);
output_asm_insn (insn_buf, operands);
}
if (b3 != 0)
{
sprintf (insn_buf, "%s\t%%z2,%%z0", opname);
output_asm_insn (insn_buf, operands);
}
}
}
break;
default:
gcc_unreachable ();
}
return "";
}
 
/* Compute the length of a logical insn. */
 
unsigned int
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. */
const unsigned HOST_WIDE_INT det = (code != AND) ? intval : ~intval;
/* Break up DET into pieces. */
const unsigned HOST_WIDE_INT b0 = (det >> 0) & 0xff;
const unsigned HOST_WIDE_INT b1 = (det >> 8) & 0xff;
const unsigned HOST_WIDE_INT b2 = (det >> 16) & 0xff;
const unsigned HOST_WIDE_INT b3 = (det >> 24) & 0xff;
const unsigned HOST_WIDE_INT w0 = (det >> 0) & 0xffff;
const unsigned HOST_WIDE_INT w1 = (det >> 16) & 0xffff;
int lower_half_easy_p = 0;
int upper_half_easy_p = 0;
/* Insn length. */
unsigned int length = 0;
 
switch (mode)
{
case HImode:
/* First, see if we can finish with one insn. */
if ((TARGET_H8300H || TARGET_H8300S)
&& b0 != 0
&& b1 != 0)
{
length = h8300_length_from_table (operands[1], operands[2],
&logicw_length_table);
}
else
{
/* Take care of the lower byte. */
if (b0 != 0)
length += 2;
 
/* Take care of the upper byte. */
if (b1 != 0)
length += 2;
}
break;
case SImode:
if (TARGET_H8300H || TARGET_H8300S)
{
/* Determine if the lower half can be taken care of in no more
than two bytes. */
lower_half_easy_p = (b0 == 0
|| b1 == 0
|| (code != IOR && w0 == 0xffff));
 
/* Determine if the upper half can be taken care of in no more
than two bytes. */
upper_half_easy_p = ((code != IOR && w1 == 0xffff)
|| (code == AND && w1 == 0xff00));
}
 
/* Check if doing everything with one insn is no worse than
using multiple insns. */
if ((TARGET_H8300H || TARGET_H8300S)
&& w0 != 0 && w1 != 0
&& !(lower_half_easy_p && upper_half_easy_p)
&& !(code == IOR && w1 == 0xffff
&& (w0 & 0x8000) != 0 && lower_half_easy_p))
{
length = h8300_length_from_table (operands[1], operands[2],
&logicl_length_table);
}
else
{
/* Take care of the lower and upper words individually. For
each word, we try different methods in the order of
 
1) the special insn (in case of AND or XOR),
2) the word-wise insn, and
3) The byte-wise insn. */
if (w0 == 0xffff
&& (TARGET_H8300 ? (code == AND) : (code != IOR)))
{
length += 2;
}
else if ((TARGET_H8300H || TARGET_H8300S)
&& (b0 != 0)
&& (b1 != 0))
{
length += 4;
}
else
{
if (b0 != 0)
length += 2;
 
if (b1 != 0)
length += 2;
}
 
if (w1 == 0xffff
&& (TARGET_H8300 ? (code == AND) : (code != IOR)))
{
length += 2;
}
else if ((TARGET_H8300H || TARGET_H8300S)
&& code == IOR
&& w1 == 0xffff
&& (w0 & 0x8000) != 0)
{
length += 2;
}
else if ((TARGET_H8300H || TARGET_H8300S)
&& code == AND
&& w1 == 0xff00)
{
length += 2;
}
else if (TARGET_H8300H || TARGET_H8300S)
{
if (w1 != 0)
length += 4;
}
else
{
if (b2 != 0)
length += 2;
 
if (b3 != 0)
length += 2;
}
}
break;
default:
gcc_unreachable ();
}
return length;
}
 
/* Compute which flag bits are valid after a logical insn. */
 
int
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. */
const unsigned HOST_WIDE_INT det = (code != AND) ? intval : ~intval;
/* Break up DET into pieces. */
const unsigned HOST_WIDE_INT b0 = (det >> 0) & 0xff;
const unsigned HOST_WIDE_INT b1 = (det >> 8) & 0xff;
const unsigned HOST_WIDE_INT w0 = (det >> 0) & 0xffff;
const unsigned HOST_WIDE_INT w1 = (det >> 16) & 0xffff;
int lower_half_easy_p = 0;
int upper_half_easy_p = 0;
/* Condition code. */
enum attr_cc cc = CC_CLOBBER;
 
switch (mode)
{
case HImode:
/* First, see if we can finish with one insn. */
if ((TARGET_H8300H || TARGET_H8300S)
&& b0 != 0
&& b1 != 0)
{
cc = CC_SET_ZNV;
}
break;
case SImode:
if (TARGET_H8300H || TARGET_H8300S)
{
/* Determine if the lower half can be taken care of in no more
than two bytes. */
lower_half_easy_p = (b0 == 0
|| b1 == 0
|| (code != IOR && w0 == 0xffff));
 
/* Determine if the upper half can be taken care of in no more
than two bytes. */
upper_half_easy_p = ((code != IOR && w1 == 0xffff)
|| (code == AND && w1 == 0xff00));
}
 
/* Check if doing everything with one insn is no worse than
using multiple insns. */
if ((TARGET_H8300H || TARGET_H8300S)
&& w0 != 0 && w1 != 0
&& !(lower_half_easy_p && upper_half_easy_p)
&& !(code == IOR && w1 == 0xffff
&& (w0 & 0x8000) != 0 && lower_half_easy_p))
{
cc = CC_SET_ZNV;
}
else
{
if ((TARGET_H8300H || TARGET_H8300S)
&& code == IOR
&& w1 == 0xffff
&& (w0 & 0x8000) != 0)
{
cc = CC_SET_ZNV;
}
}
break;
default:
gcc_unreachable ();
}
return cc;
}
/* 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));
}
/* Shifts.
 
We devote a fair bit of code to getting efficient shifts since we
can only shift one bit at a time on the H8/300 and H8/300H and only
one or two bits at a time on the H8S.
 
All shift code falls into one of the following ways of
implementation:
 
o SHIFT_INLINE: Emit straight line code for the shift; this is used
when a straight line shift is about the same size or smaller than
a loop.
 
o SHIFT_ROT_AND: Rotate the value the opposite direction, then mask
off the bits we don't need. This is used when only a few of the
bits in the original value will survive in the shifted value.
 
o SHIFT_SPECIAL: Often it's possible to move a byte or a word to
simulate a shift by 8, 16, or 24 bits. Once moved, a few inline
shifts can be added if the shift count is slightly more than 8 or
16. This case also includes other oddballs that are not worth
explaining here.
 
o SHIFT_LOOP: Emit a loop using one (or two on H8S) bit shifts.
 
For each shift count, we try to use code that has no trade-off
between code size and speed whenever possible.
 
If the trade-off is unavoidable, we try to be reasonable.
Specifically, the fastest version is one instruction longer than
the shortest version, we take the fastest version. We also provide
the use a way to switch back to the shortest version with -Os.
 
For the details of the shift algorithms for various shift counts,
refer to shift_alg_[qhs]i. */
 
/* 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)
{
if (!TARGET_H8300SX)
return H8SX_SHIFT_NONE;
 
switch (code)
{
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:
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:
gcc_unreachable ();
}
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. */
 
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
code at emit time, but need to allocate a scratch reg now. */
 
emit_insn (gen_rtx_PARALLEL
(VOIDmode,
gen_rtvec (2,
gen_rtx_SET (VOIDmode, operands[0],
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. */
 
enum shift_mode
{
QIshift, HIshift, SIshift
};
 
/* For single bit shift insns, record assembler and what bits of the
condition code are valid afterwards (represented as various CC_FOO
bits, 0 means CC isn't left in a usable state). */
 
struct shift_insn
{
const char *const assembler;
const int cc_valid;
};
 
/* Assembler instruction shift table.
 
These tables are used to look up the basic shifts.
They are indexed by cpu, shift_type, and mode. */
 
static const struct shift_insn shift_one[2][3][3] =
{
/* H8/300 */
{
/* SHIFT_ASHIFT */
{
{ "shll\t%X0", CC_SET_ZNV },
{ "add.w\t%T0,%T0", CC_SET_ZN },
{ "add.w\t%f0,%f0\n\taddx\t%y0,%y0\n\taddx\t%z0,%z0", CC_CLOBBER }
},
/* SHIFT_LSHIFTRT */
{
{ "shlr\t%X0", CC_SET_ZNV },
{ "shlr\t%t0\n\trotxr\t%s0", CC_CLOBBER },
{ "shlr\t%z0\n\trotxr\t%y0\n\trotxr\t%x0\n\trotxr\t%w0", CC_CLOBBER }
},
/* SHIFT_ASHIFTRT */
{
{ "shar\t%X0", CC_SET_ZNV },
{ "shar\t%t0\n\trotxr\t%s0", CC_CLOBBER },
{ "shar\t%z0\n\trotxr\t%y0\n\trotxr\t%x0\n\trotxr\t%w0", CC_CLOBBER }
}
},
/* H8/300H */
{
/* SHIFT_ASHIFT */
{
{ "shll.b\t%X0", CC_SET_ZNV },
{ "shll.w\t%T0", CC_SET_ZNV },
{ "shll.l\t%S0", CC_SET_ZNV }
},
/* SHIFT_LSHIFTRT */
{
{ "shlr.b\t%X0", CC_SET_ZNV },
{ "shlr.w\t%T0", CC_SET_ZNV },
{ "shlr.l\t%S0", CC_SET_ZNV }
},
/* SHIFT_ASHIFTRT */
{
{ "shar.b\t%X0", CC_SET_ZNV },
{ "shar.w\t%T0", CC_SET_ZNV },
{ "shar.l\t%S0", CC_SET_ZNV }
}
}
};
 
static const struct shift_insn shift_two[3][3] =
{
/* SHIFT_ASHIFT */
{
{ "shll.b\t#2,%X0", CC_SET_ZNV },
{ "shll.w\t#2,%T0", CC_SET_ZNV },
{ "shll.l\t#2,%S0", CC_SET_ZNV }
},
/* SHIFT_LSHIFTRT */
{
{ "shlr.b\t#2,%X0", CC_SET_ZNV },
{ "shlr.w\t#2,%T0", CC_SET_ZNV },
{ "shlr.l\t#2,%S0", CC_SET_ZNV }
},
/* SHIFT_ASHIFTRT */
{
{ "shar.b\t#2,%X0", CC_SET_ZNV },
{ "shar.w\t#2,%T0", CC_SET_ZNV },
{ "shar.l\t#2,%S0", CC_SET_ZNV }
}
};
 
/* Rotates are organized by which shift they'll be used in implementing.
There's no need to record whether the cc is valid afterwards because
it is the AND insn that will decide this. */
 
static const char *const rotate_one[2][3][3] =
{
/* H8/300 */
{
/* SHIFT_ASHIFT */
{
"rotr\t%X0",
"shlr\t%t0\n\trotxr\t%s0\n\tbst\t#7,%t0",
0
},
/* SHIFT_LSHIFTRT */
{
"rotl\t%X0",
"shll\t%s0\n\trotxl\t%t0\n\tbst\t#0,%s0",
0
},
/* SHIFT_ASHIFTRT */
{
"rotl\t%X0",
"shll\t%s0\n\trotxl\t%t0\n\tbst\t#0,%s0",
0
}
},
/* H8/300H */
{
/* SHIFT_ASHIFT */
{
"rotr.b\t%X0",
"rotr.w\t%T0",
"rotr.l\t%S0"
},
/* SHIFT_LSHIFTRT */
{
"rotl.b\t%X0",
"rotl.w\t%T0",
"rotl.l\t%S0"
},
/* SHIFT_ASHIFTRT */
{
"rotl.b\t%X0",
"rotl.w\t%T0",
"rotl.l\t%S0"
}
}
};
 
static const char *const rotate_two[3][3] =
{
/* SHIFT_ASHIFT */
{
"rotr.b\t#2,%X0",
"rotr.w\t#2,%T0",
"rotr.l\t#2,%S0"
},
/* SHIFT_LSHIFTRT */
{
"rotl.b\t#2,%X0",
"rotl.w\t#2,%T0",
"rotl.l\t#2,%S0"
},
/* SHIFT_ASHIFTRT */
{
"rotl.b\t#2,%X0",
"rotl.w\t#2,%T0",
"rotl.l\t#2,%S0"
}
};
 
struct shift_info {
/* Shift algorithm. */
enum shift_alg alg;
 
/* The number of bits to be shifted by shift1 and shift2. Valid
when ALG is SHIFT_SPECIAL. */
unsigned int remainder;
 
/* Special insn for a shift. Valid when ALG is SHIFT_SPECIAL. */
const char *special;
 
/* Insn for a one-bit shift. Valid when ALG is either SHIFT_INLINE
or SHIFT_SPECIAL, and REMAINDER is nonzero. */
const char *shift1;
 
/* Insn for a two-bit shift. Valid when ALG is either SHIFT_INLINE
or SHIFT_SPECIAL, and REMAINDER is nonzero. */
const char *shift2;
 
/* CC status for SHIFT_INLINE. */
int cc_inline;
 
/* CC status for SHIFT_SPECIAL. */
int cc_special;
};
 
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
in the pointers in INFO. We achieve the maximum efficiency in most
cases when !TARGET_H8300. In case of TARGET_H8300, shifts in
SImode in particular have a lot of room to optimize.
 
We first determine the strategy of the shift algorithm by a table
lookup. If that tells us to use a hand crafted assembly code, we
go into the big switch statement to find what that is. Otherwise,
we resort to a generic way, such as inlining. In either case, the
result is returned through INFO. */
 
static void
get_shift_alg (enum shift_type shift_type, enum shift_mode shift_mode,
unsigned int count, struct shift_info *info)
{
enum h8_cpu cpu;
 
/* Find the target CPU. */
if (TARGET_H8300)
cpu = H8_300;
else if (TARGET_H8300H)
cpu = H8_300H;
else
cpu = H8_S;
 
/* Find the shift algorithm. */
info->alg = SHIFT_LOOP;
switch (shift_mode)
{
case QIshift:
if (count < GET_MODE_BITSIZE (QImode))
info->alg = shift_alg_qi[cpu][shift_type][count];
break;
 
case HIshift:
if (count < GET_MODE_BITSIZE (HImode))
info->alg = shift_alg_hi[cpu][shift_type][count];
break;
 
case SIshift:
if (count < GET_MODE_BITSIZE (SImode))
info->alg = shift_alg_si[cpu][shift_type][count];
break;
 
default:
gcc_unreachable ();
}
 
/* Fill in INFO. Return unless we have SHIFT_SPECIAL. */
switch (info->alg)
{
case SHIFT_INLINE:
info->remainder = count;
/* Fall through. */
 
case SHIFT_LOOP:
/* It is up to the caller to know that looping clobbers cc. */
info->shift1 = shift_one[cpu_type][shift_type][shift_mode].assembler;
info->shift2 = shift_two[shift_type][shift_mode].assembler;
info->cc_inline = shift_one[cpu_type][shift_type][shift_mode].cc_valid;
goto end;
 
case SHIFT_ROT_AND:
info->shift1 = rotate_one[cpu_type][shift_type][shift_mode];
info->shift2 = rotate_two[shift_type][shift_mode];
info->cc_inline = CC_CLOBBER;
goto end;
 
case SHIFT_SPECIAL:
/* REMAINDER is 0 for most cases, so initialize it to 0. */
info->remainder = 0;
info->shift1 = shift_one[cpu_type][shift_type][shift_mode].assembler;
info->shift2 = shift_two[shift_type][shift_mode].assembler;
info->cc_inline = shift_one[cpu_type][shift_type][shift_mode].cc_valid;
info->cc_special = CC_CLOBBER;
break;
}
 
/* Here we only deal with SHIFT_SPECIAL. */
switch (shift_mode)
{
case QIshift:
/* For ASHIFTRT by 7 bits, the sign bit is simply replicated
through the entire value. */
gcc_assert (shift_type == SHIFT_ASHIFTRT && count == 7);
info->special = "shll\t%X0\n\tsubx\t%X0,%X0";
goto end;
 
case HIshift:
if (count == 7)
{
switch (shift_type)
{
case SHIFT_ASHIFT:
if (TARGET_H8300)
info->special = "shar.b\t%t0\n\tmov.b\t%s0,%t0\n\trotxr.b\t%t0\n\trotr.b\t%s0\n\tand.b\t#0x80,%s0";
else
info->special = "shar.b\t%t0\n\tmov.b\t%s0,%t0\n\trotxr.w\t%T0\n\tand.b\t#0x80,%s0";
goto end;
case SHIFT_LSHIFTRT:
if (TARGET_H8300)
info->special = "shal.b\t%s0\n\tmov.b\t%t0,%s0\n\trotxl.b\t%s0\n\trotl.b\t%t0\n\tand.b\t#0x01,%t0";
else
info->special = "shal.b\t%s0\n\tmov.b\t%t0,%s0\n\trotxl.w\t%T0\n\tand.b\t#0x01,%t0";
goto end;
case SHIFT_ASHIFTRT:
info->special = "shal.b\t%s0\n\tmov.b\t%t0,%s0\n\trotxl.b\t%s0\n\tsubx\t%t0,%t0";
goto end;
}
}
else if ((8 <= count && count <= 13)
|| (TARGET_H8300S && count == 14))
{
info->remainder = count - 8;
 
switch (shift_type)
{
case SHIFT_ASHIFT:
info->special = "mov.b\t%s0,%t0\n\tsub.b\t%s0,%s0";
goto end;
case SHIFT_LSHIFTRT:
if (TARGET_H8300)
{
info->special = "mov.b\t%t0,%s0\n\tsub.b\t%t0,%t0";
info->shift1 = "shlr.b\t%s0";
info->cc_inline = CC_SET_ZNV;
}
else
{
info->special = "mov.b\t%t0,%s0\n\textu.w\t%T0";
info->cc_special = CC_SET_ZNV;
}
goto end;
case SHIFT_ASHIFTRT:
if (TARGET_H8300)
{
info->special = "mov.b\t%t0,%s0\n\tbld\t#7,%s0\n\tsubx\t%t0,%t0";
info->shift1 = "shar.b\t%s0";
}
else
{
info->special = "mov.b\t%t0,%s0\n\texts.w\t%T0";
info->cc_special = CC_SET_ZNV;
}
goto end;
}
}
else if (count == 14)
{
switch (shift_type)
{
case SHIFT_ASHIFT:
if (TARGET_H8300)
info->special = "mov.b\t%s0,%t0\n\trotr.b\t%t0\n\trotr.b\t%t0\n\tand.b\t#0xC0,%t0\n\tsub.b\t%s0,%s0";
goto end;
case SHIFT_LSHIFTRT:
if (TARGET_H8300)
info->special = "mov.b\t%t0,%s0\n\trotl.b\t%s0\n\trotl.b\t%s0\n\tand.b\t#3,%s0\n\tsub.b\t%t0,%t0";
goto end;
case SHIFT_ASHIFTRT:
if (TARGET_H8300)
info->special = "mov.b\t%t0,%s0\n\tshll.b\t%s0\n\tsubx.b\t%t0,%t0\n\tshll.b\t%s0\n\tmov.b\t%t0,%s0\n\tbst.b\t#0,%s0";
else if (TARGET_H8300H)
{
info->special = "shll.b\t%t0\n\tsubx.b\t%s0,%s0\n\tshll.b\t%t0\n\trotxl.b\t%s0\n\texts.w\t%T0";
info->cc_special = CC_SET_ZNV;
}
else /* TARGET_H8300S */
gcc_unreachable ();
goto end;
}
}
else if (count == 15)
{
switch (shift_type)
{
case SHIFT_ASHIFT:
info->special = "bld\t#0,%s0\n\txor\t%s0,%s0\n\txor\t%t0,%t0\n\tbst\t#7,%t0";
goto end;
case SHIFT_LSHIFTRT:
info->special = "bld\t#7,%t0\n\txor\t%s0,%s0\n\txor\t%t0,%t0\n\tbst\t#0,%s0";
goto end;
case SHIFT_ASHIFTRT:
info->special = "shll\t%t0\n\tsubx\t%t0,%t0\n\tmov.b\t%t0,%s0";
goto end;
}
}
gcc_unreachable ();
 
case SIshift:
if (TARGET_H8300 && 8 <= count && count <= 9)
{
info->remainder = count - 8;
 
switch (shift_type)
{
case SHIFT_ASHIFT:
info->special = "mov.b\t%y0,%z0\n\tmov.b\t%x0,%y0\n\tmov.b\t%w0,%x0\n\tsub.b\t%w0,%w0";
goto end;
case SHIFT_LSHIFTRT:
info->special = "mov.b\t%x0,%w0\n\tmov.b\t%y0,%x0\n\tmov.b\t%z0,%y0\n\tsub.b\t%z0,%z0";
info->shift1 = "shlr\t%y0\n\trotxr\t%x0\n\trotxr\t%w0";
goto end;
case SHIFT_ASHIFTRT:
info->special = "mov.b\t%x0,%w0\n\tmov.b\t%y0,%x0\n\tmov.b\t%z0,%y0\n\tshll\t%z0\n\tsubx\t%z0,%z0";
goto end;
}
}
else if (count == 8 && !TARGET_H8300)
{
switch (shift_type)
{
case SHIFT_ASHIFT:
info->special = "mov.w\t%e0,%f4\n\tmov.b\t%s4,%t4\n\tmov.b\t%t0,%s4\n\tmov.b\t%s0,%t0\n\tsub.b\t%s0,%s0\n\tmov.w\t%f4,%e0";
goto end;
case SHIFT_LSHIFTRT:
info->special = "mov.w\t%e0,%f4\n\tmov.b\t%t0,%s0\n\tmov.b\t%s4,%t0\n\tmov.b\t%t4,%s4\n\textu.w\t%f4\n\tmov.w\t%f4,%e0";
goto end;
case SHIFT_ASHIFTRT:
info->special = "mov.w\t%e0,%f4\n\tmov.b\t%t0,%s0\n\tmov.b\t%s4,%t0\n\tmov.b\t%t4,%s4\n\texts.w\t%f4\n\tmov.w\t%f4,%e0";
goto end;
}
}
else if (count == 15 && TARGET_H8300)
{
switch (shift_type)
{
case SHIFT_ASHIFT:
gcc_unreachable ();
case SHIFT_LSHIFTRT:
info->special = "bld\t#7,%z0\n\tmov.w\t%e0,%f0\n\txor\t%y0,%y0\n\txor\t%z0,%z0\n\trotxl\t%w0\n\trotxl\t%x0\n\trotxl\t%y0";
goto end;
case SHIFT_ASHIFTRT:
info->special = "bld\t#7,%z0\n\tmov.w\t%e0,%f0\n\trotxl\t%w0\n\trotxl\t%x0\n\tsubx\t%y0,%y0\n\tsubx\t%z0,%z0";
goto end;
}
}
else if (count == 15 && !TARGET_H8300)
{
switch (shift_type)
{
case SHIFT_ASHIFT:
info->special = "shlr.w\t%e0\n\tmov.w\t%f0,%e0\n\txor.w\t%f0,%f0\n\trotxr.l\t%S0";
info->cc_special = CC_SET_ZNV;
goto end;
case SHIFT_LSHIFTRT:
info->special = "shll.w\t%f0\n\tmov.w\t%e0,%f0\n\txor.w\t%e0,%e0\n\trotxl.l\t%S0";
info->cc_special = CC_SET_ZNV;
goto end;
case SHIFT_ASHIFTRT:
gcc_unreachable ();
}
}
else if ((TARGET_H8300 && 16 <= count && count <= 20)
|| (TARGET_H8300H && 16 <= count && count <= 19)
|| (TARGET_H8300S && 16 <= count && count <= 21))
{
info->remainder = count - 16;
 
switch (shift_type)
{
case SHIFT_ASHIFT:
info->special = "mov.w\t%f0,%e0\n\tsub.w\t%f0,%f0";
if (TARGET_H8300)
info->shift1 = "add.w\t%e0,%e0";
goto end;
case SHIFT_LSHIFTRT:
if (TARGET_H8300)
{
info->special = "mov.w\t%e0,%f0\n\tsub.w\t%e0,%e0";
info->shift1 = "shlr\t%x0\n\trotxr\t%w0";
}
else
{
info->special = "mov.w\t%e0,%f0\n\textu.l\t%S0";
info->cc_special = CC_SET_ZNV;
}
goto end;
case SHIFT_ASHIFTRT:
if (TARGET_H8300)
{
info->special = "mov.w\t%e0,%f0\n\tshll\t%z0\n\tsubx\t%z0,%z0\n\tmov.b\t%z0,%y0";
info->shift1 = "shar\t%x0\n\trotxr\t%w0";
}
else
{
info->special = "mov.w\t%e0,%f0\n\texts.l\t%S0";
info->cc_special = CC_SET_ZNV;
}
goto end;
}
}
else if (TARGET_H8300 && 24 <= count && count <= 28)
{
info->remainder = count - 24;
 
switch (shift_type)
{
case SHIFT_ASHIFT:
info->special = "mov.b\t%w0,%z0\n\tsub.b\t%y0,%y0\n\tsub.w\t%f0,%f0";
info->shift1 = "shll.b\t%z0";
info->cc_inline = CC_SET_ZNV;
goto end;
case SHIFT_LSHIFTRT:
info->special = "mov.b\t%z0,%w0\n\tsub.b\t%x0,%x0\n\tsub.w\t%e0,%e0";
info->shift1 = "shlr.b\t%w0";
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->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))
{
info->remainder = count - 24;
 
switch (shift_type)
{
case SHIFT_ASHIFT:
info->special = "mov.b\t%s0,%t0\n\tsub.b\t%s0,%s0\n\tmov.w\t%f0,%e0\n\tsub.w\t%f0,%f0";
goto end;
case SHIFT_LSHIFTRT:
info->special = "mov.w\t%e0,%f0\n\tmov.b\t%t0,%s0\n\textu.w\t%f0\n\textu.l\t%S0";
info->cc_special = CC_SET_ZNV;
goto end;
case SHIFT_ASHIFTRT:
info->special = "mov.w\t%e0,%f0\n\tmov.b\t%t0,%s0\n\texts.w\t%f0\n\texts.l\t%S0";
info->cc_special = CC_SET_ZNV;
goto end;
}
}
else if (!TARGET_H8300 && count == 28)
{
switch (shift_type)
{
case SHIFT_ASHIFT:
if (TARGET_H8300H)
info->special = "sub.w\t%e0,%e0\n\trotr.l\t%S0\n\trotr.l\t%S0\n\trotr.l\t%S0\n\trotr.l\t%S0\n\tsub.w\t%f0,%f0";
else
info->special = "sub.w\t%e0,%e0\n\trotr.l\t#2,%S0\n\trotr.l\t#2,%S0\n\tsub.w\t%f0,%f0";
goto end;
case SHIFT_LSHIFTRT:
if (TARGET_H8300H)
{
info->special = "sub.w\t%f0,%f0\n\trotl.l\t%S0\n\trotl.l\t%S0\n\trotl.l\t%S0\n\trotl.l\t%S0\n\textu.l\t%S0";
info->cc_special = CC_SET_ZNV;
}
else
info->special = "sub.w\t%f0,%f0\n\trotl.l\t#2,%S0\n\trotl.l\t#2,%S0\n\textu.l\t%S0";
goto end;
case SHIFT_ASHIFTRT:
gcc_unreachable ();
}
}
else if (!TARGET_H8300 && count == 29)
{
switch (shift_type)
{
case SHIFT_ASHIFT:
if (TARGET_H8300H)
info->special = "sub.w\t%e0,%e0\n\trotr.l\t%S0\n\trotr.l\t%S0\n\trotr.l\t%S0\n\tsub.w\t%f0,%f0";
else
info->special = "sub.w\t%e0,%e0\n\trotr.l\t#2,%S0\n\trotr.l\t%S0\n\tsub.w\t%f0,%f0";
goto end;
case SHIFT_LSHIFTRT:
if (TARGET_H8300H)
{
info->special = "sub.w\t%f0,%f0\n\trotl.l\t%S0\n\trotl.l\t%S0\n\trotl.l\t%S0\n\textu.l\t%S0";
info->cc_special = CC_SET_ZNV;
}
else
{
info->special = "sub.w\t%f0,%f0\n\trotl.l\t#2,%S0\n\trotl.l\t%S0\n\textu.l\t%S0";
info->cc_special = CC_SET_ZNV;
}
goto end;
case SHIFT_ASHIFTRT:
gcc_unreachable ();
}
}
else if (!TARGET_H8300 && count == 30)
{
switch (shift_type)
{
case SHIFT_ASHIFT:
if (TARGET_H8300H)
info->special = "sub.w\t%e0,%e0\n\trotr.l\t%S0\n\trotr.l\t%S0\n\tsub.w\t%f0,%f0";
else
info->special = "sub.w\t%e0,%e0\n\trotr.l\t#2,%S0\n\tsub.w\t%f0,%f0";
goto end;
case SHIFT_LSHIFTRT:
if (TARGET_H8300H)
info->special = "sub.w\t%f0,%f0\n\trotl.l\t%S0\n\trotl.l\t%S0\n\textu.l\t%S0";
else
info->special = "sub.w\t%f0,%f0\n\trotl.l\t#2,%S0\n\textu.l\t%S0";
goto end;
case SHIFT_ASHIFTRT:
gcc_unreachable ();
}
}
else if (count == 31)
{
if (TARGET_H8300)
{
switch (shift_type)
{
case SHIFT_ASHIFT:
info->special = "sub.w\t%e0,%e0\n\tshlr\t%w0\n\tmov.w\t%e0,%f0\n\trotxr\t%z0";
goto end;
case SHIFT_LSHIFTRT:
info->special = "sub.w\t%f0,%f0\n\tshll\t%z0\n\tmov.w\t%f0,%e0\n\trotxl\t%w0";
goto end;
case SHIFT_ASHIFTRT:
info->special = "shll\t%z0\n\tsubx\t%w0,%w0\n\tmov.b\t%w0,%x0\n\tmov.w\t%f0,%e0";
goto end;
}
}
else
{
switch (shift_type)
{
case SHIFT_ASHIFT:
info->special = "shlr.l\t%S0\n\txor.l\t%S0,%S0\n\trotxr.l\t%S0";
info->cc_special = CC_SET_ZNV;
goto end;
case SHIFT_LSHIFTRT:
info->special = "shll.l\t%S0\n\txor.l\t%S0,%S0\n\trotxl.l\t%S0";
info->cc_special = CC_SET_ZNV;
goto end;
case SHIFT_ASHIFTRT:
info->special = "shll\t%e0\n\tsubx\t%w0,%w0\n\texts.w\t%T0\n\texts.l\t%S0";
info->cc_special = CC_SET_ZNV;
goto end;
}
}
}
gcc_unreachable ();
 
default:
gcc_unreachable ();
}
 
end:
if (!TARGET_H8300S)
info->shift2 = NULL;
}
 
/* Given COUNT and MODE of a shift, return 1 if a scratch reg may be
needed for some shift with COUNT and MODE. Return 0 otherwise. */
 
int
h8300_shift_needs_scratch_p (int count, enum machine_mode mode)
{
enum h8_cpu cpu;
int a, lr, ar;
 
if (GET_MODE_BITSIZE (mode) <= count)
return 1;
 
/* Find out the target CPU. */
if (TARGET_H8300)
cpu = H8_300;
else if (TARGET_H8300H)
cpu = H8_300H;
else
cpu = H8_S;
 
/* Find the shift algorithm. */
switch (mode)
{
case QImode:
a = shift_alg_qi[cpu][SHIFT_ASHIFT][count];
lr = shift_alg_qi[cpu][SHIFT_LSHIFTRT][count];
ar = shift_alg_qi[cpu][SHIFT_ASHIFTRT][count];
break;
 
case HImode:
a = shift_alg_hi[cpu][SHIFT_ASHIFT][count];
lr = shift_alg_hi[cpu][SHIFT_LSHIFTRT][count];
ar = shift_alg_hi[cpu][SHIFT_ASHIFTRT][count];
break;
 
case SImode:
a = shift_alg_si[cpu][SHIFT_ASHIFT][count];
lr = shift_alg_si[cpu][SHIFT_LSHIFTRT][count];
ar = shift_alg_si[cpu][SHIFT_ASHIFTRT][count];
break;
 
default:
gcc_unreachable ();
}
 
/* On H8/300H, count == 8 uses a scratch register. */
return (a == SHIFT_LOOP || lr == SHIFT_LOOP || ar == SHIFT_LOOP
|| (TARGET_H8300H && mode == SImode && count == 8));
}
 
/* Output the assembler code for doing shifts. */
 
const char *
output_a_shift (rtx *operands)
{
static int loopend_lab;
rtx shift = operands[3];
enum machine_mode mode = GET_MODE (shift);
enum rtx_code code = GET_CODE (shift);
enum shift_type shift_type;
enum shift_mode shift_mode;
struct shift_info info;
int n;
 
loopend_lab++;
 
switch (mode)
{
case QImode:
shift_mode = QIshift;
break;
case HImode:
shift_mode = HIshift;
break;
case SImode:
shift_mode = SIshift;
break;
default:
gcc_unreachable ();
}
 
switch (code)
{
case ASHIFTRT:
shift_type = SHIFT_ASHIFTRT;
break;
case LSHIFTRT:
shift_type = SHIFT_LSHIFTRT;
break;
case ASHIFT:
shift_type = SHIFT_ASHIFT;
break;
default:
gcc_unreachable ();
}
 
/* This case must be taken care of by one of the two splitters
that convert a variable shift into a loop. */
gcc_assert (GET_CODE (operands[2]) == CONST_INT);
n = INTVAL (operands[2]);
 
/* If the count is negative, make it 0. */
if (n < 0)
n = 0;
/* If the count is too big, truncate it.
ANSI says shifts of GET_MODE_BITSIZE are undefined - we choose to
do the intuitive thing. */
else if ((unsigned int) n > GET_MODE_BITSIZE (mode))
n = GET_MODE_BITSIZE (mode);
 
get_shift_alg (shift_type, shift_mode, n, &info);
switch (info.alg)
{
case SHIFT_SPECIAL:
output_asm_insn (info.special, operands);
/* Fall through. */
 
case SHIFT_INLINE:
n = info.remainder;
 
/* Emit two bit shifts first. */
if (info.shift2 != NULL)
{
for (; n > 1; n -= 2)
output_asm_insn (info.shift2, operands);
}
 
/* Now emit one bit shifts for any residual. */
for (; n > 0; n--)
output_asm_insn (info.shift1, operands);
return "";
case SHIFT_ROT_AND:
{
int m = GET_MODE_BITSIZE (mode) - n;
const int mask = (shift_type == SHIFT_ASHIFT
? ((1 << m) - 1) << n
: (1 << m) - 1);
char insn_buf[200];
 
/* Not all possibilities of rotate are supported. They shouldn't
be generated, but let's watch for 'em. */
gcc_assert (info.shift1);
/* Emit two bit rotates first. */
if (info.shift2 != NULL)
{
for (; m > 1; m -= 2)
output_asm_insn (info.shift2, operands);
}
/* Now single bit rotates for any residual. */
for (; m > 0; m--)
output_asm_insn (info.shift1, operands);
/* Now mask off the high bits. */
switch (mode)
{
case QImode:
sprintf (insn_buf, "and\t#%d,%%X0", mask);
break;
 
case HImode:
gcc_assert (TARGET_H8300H || TARGET_H8300S);
sprintf (insn_buf, "and.w\t#%d,%%T0", mask);
break;
 
default:
gcc_unreachable ();
}
 
output_asm_insn (insn_buf, operands);
return "";
}
 
case SHIFT_LOOP:
/* A loop to shift by a "large" constant value.
If we have shift-by-2 insns, use them. */
if (info.shift2 != NULL)
{
fprintf (asm_out_file, "\tmov.b #%d,%sl\n", n / 2,
names_big[REGNO (operands[4])]);
fprintf (asm_out_file, ".Llt%d:\n", loopend_lab);
output_asm_insn (info.shift2, operands);
output_asm_insn ("add #0xff,%X4", operands);
fprintf (asm_out_file, "\tbne .Llt%d\n", loopend_lab);
if (n % 2)
output_asm_insn (info.shift1, operands);
}
else
{
fprintf (asm_out_file, "\tmov.b #%d,%sl\n", n,
names_big[REGNO (operands[4])]);
fprintf (asm_out_file, ".Llt%d:\n", loopend_lab);
output_asm_insn (info.shift1, operands);
output_asm_insn ("add #0xff,%X4", operands);
fprintf (asm_out_file, "\tbne .Llt%d\n", loopend_lab);
}
return "";
default:
gcc_unreachable ();
}
}
 
/* Count the number of assembly instructions in a string TEMPLATE. */
 
static unsigned int
h8300_asm_insn_count (const char *template)
{
unsigned int count = 1;
 
for (; *template; template++)
if (*template == '\n')
count++;
 
return count;
}
 
/* Compute the length of a shift insn. */
 
unsigned int
compute_a_shift_length (rtx insn ATTRIBUTE_UNUSED, rtx *operands)
{
rtx shift = operands[3];
enum machine_mode mode = GET_MODE (shift);
enum rtx_code code = GET_CODE (shift);
enum shift_type shift_type;
enum shift_mode shift_mode;
struct shift_info info;
unsigned int wlength = 0;
 
switch (mode)
{
case QImode:
shift_mode = QIshift;
break;
case HImode:
shift_mode = HIshift;
break;
case SImode:
shift_mode = SIshift;
break;
default:
gcc_unreachable ();
}
 
switch (code)
{
case ASHIFTRT:
shift_type = SHIFT_ASHIFTRT;
break;
case LSHIFTRT:
shift_type = SHIFT_LSHIFTRT;
break;
case ASHIFT:
shift_type = SHIFT_ASHIFT;
break;
default:
gcc_unreachable ();
}
 
if (GET_CODE (operands[2]) != CONST_INT)
{
/* Get the assembler code to do one shift. */
get_shift_alg (shift_type, shift_mode, 1, &info);
 
return (4 + h8300_asm_insn_count (info.shift1)) * 2;
}
else
{
int n = INTVAL (operands[2]);
 
/* If the count is negative, make it 0. */
if (n < 0)
n = 0;
/* If the count is too big, truncate it.
ANSI says shifts of GET_MODE_BITSIZE are undefined - we choose to
do the intuitive thing. */
else if ((unsigned int) n > GET_MODE_BITSIZE (mode))
n = GET_MODE_BITSIZE (mode);
 
get_shift_alg (shift_type, shift_mode, n, &info);
 
switch (info.alg)
{
case SHIFT_SPECIAL:
wlength += h8300_asm_insn_count (info.special);
 
/* Every assembly instruction used in SHIFT_SPECIAL case
takes 2 bytes except xor.l, which takes 4 bytes, so if we
see xor.l, we just pretend that xor.l counts as two insns
so that the insn length will be computed correctly. */
if (strstr (info.special, "xor.l") != NULL)
wlength++;
 
/* Fall through. */
 
case SHIFT_INLINE:
n = info.remainder;
 
if (info.shift2 != NULL)
{
wlength += h8300_asm_insn_count (info.shift2) * (n / 2);
n = n % 2;
}
 
wlength += h8300_asm_insn_count (info.shift1) * n;
 
return 2 * wlength;
 
case SHIFT_ROT_AND:
{
int m = GET_MODE_BITSIZE (mode) - n;
 
/* Not all possibilities of rotate are supported. They shouldn't
be generated, but let's watch for 'em. */
gcc_assert (info.shift1);
 
if (info.shift2 != NULL)
{
wlength += h8300_asm_insn_count (info.shift2) * (m / 2);
m = m % 2;
}
 
wlength += h8300_asm_insn_count (info.shift1) * m;
 
/* Now mask off the high bits. */
switch (mode)
{
case QImode:
wlength += 1;
break;
case HImode:
wlength += 2;
break;
case SImode:
gcc_assert (!TARGET_H8300);
wlength += 3;
break;
default:
gcc_unreachable ();
}
return 2 * wlength;
}
 
case SHIFT_LOOP:
/* A loop to shift by a "large" constant value.
If we have shift-by-2 insns, use them. */
if (info.shift2 != NULL)
{
wlength += 3 + h8300_asm_insn_count (info.shift2);
if (n % 2)
wlength += h8300_asm_insn_count (info.shift1);
}
else
{
wlength += 3 + h8300_asm_insn_count (info.shift1);
}
return 2 * wlength;
 
default:
gcc_unreachable ();
}
}
}
 
/* Compute which flag bits are valid after a shift insn. */
 
int
compute_a_shift_cc (rtx insn ATTRIBUTE_UNUSED, rtx *operands)
{
rtx shift = operands[3];
enum machine_mode mode = GET_MODE (shift);
enum rtx_code code = GET_CODE (shift);
enum shift_type shift_type;
enum shift_mode shift_mode;
struct shift_info info;
int n;
switch (mode)
{
case QImode:
shift_mode = QIshift;
break;
case HImode:
shift_mode = HIshift;
break;
case SImode:
shift_mode = SIshift;
break;
default:
gcc_unreachable ();
}
 
switch (code)
{
case ASHIFTRT:
shift_type = SHIFT_ASHIFTRT;
break;
case LSHIFTRT:
shift_type = SHIFT_LSHIFTRT;
break;
case ASHIFT:
shift_type = SHIFT_ASHIFT;
break;
default:
gcc_unreachable ();
}
 
/* This case must be taken care of by one of the two splitters
that convert a variable shift into a loop. */
gcc_assert (GET_CODE (operands[2]) == CONST_INT);
n = INTVAL (operands[2]);
 
/* If the count is negative, make it 0. */
if (n < 0)
n = 0;
/* If the count is too big, truncate it.
ANSI says shifts of GET_MODE_BITSIZE are undefined - we choose to
do the intuitive thing. */
else if ((unsigned int) n > GET_MODE_BITSIZE (mode))
n = GET_MODE_BITSIZE (mode);
get_shift_alg (shift_type, shift_mode, n, &info);
switch (info.alg)
{
case SHIFT_SPECIAL:
if (info.remainder == 0)
return info.cc_special;
 
/* Fall through. */
 
case SHIFT_INLINE:
return info.cc_inline;
case SHIFT_ROT_AND:
/* This case always ends with an and instruction. */
return CC_SET_ZNV;
case SHIFT_LOOP:
/* A loop to shift by a "large" constant value.
If we have shift-by-2 insns, use them. */
if (info.shift2 != NULL)
{
if (n % 2)
return info.cc_inline;
}
return CC_CLOBBER;
default:
gcc_unreachable ();
}
}
/* 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
output_a_rotate () at the insn emit time. */
 
int
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);
 
if (h8sx_classify_shift (mode, ROTATE, rotate_amount) == H8SX_SHIFT_UNARY)
return false;
 
/* We rotate in place. */
emit_move_insn (dst, src);
 
if (GET_CODE (rotate_amount) != CONST_INT)
{
rtx counter = gen_reg_rtx (QImode);
rtx start_label = gen_label_rtx ();
rtx end_label = gen_label_rtx ();
 
/* If the rotate amount is less than or equal to 0,
we go out of the loop. */
emit_cmp_and_jump_insns (rotate_amount, const0_rtx, LE, NULL_RTX,
QImode, 0, end_label);
 
/* Initialize the loop counter. */
emit_move_insn (counter, rotate_amount);
 
emit_label (start_label);
 
/* Rotate by one bit. */
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:
gcc_unreachable ();
}
 
/* Decrement the counter by 1. */
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, const0_rtx, NE, NULL_RTX, QImode, 1,
start_label);
 
emit_label (end_label);
}
else
{
/* Rotate by AMOUNT bits. */
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:
gcc_unreachable ();
}
}
 
return 1;
}
 
/* Output a rotate insn. */
 
const char *
output_a_rotate (enum rtx_code code, rtx *operands)
{
rtx dst = operands[0];
rtx rotate_amount = operands[2];
enum shift_mode rotate_mode;
enum shift_type rotate_type;
const char *insn_buf;
int bits;
int amount;
enum machine_mode mode = GET_MODE (dst);
 
gcc_assert (GET_CODE (rotate_amount) == CONST_INT);
 
switch (mode)
{
case QImode:
rotate_mode = QIshift;
break;
case HImode:
rotate_mode = HIshift;
break;
case SImode:
rotate_mode = SIshift;
break;
default:
gcc_unreachable ();
}
 
switch (code)
{
case ROTATERT:
rotate_type = SHIFT_ASHIFT;
break;
case ROTATE:
rotate_type = SHIFT_LSHIFTRT;
break;
default:
gcc_unreachable ();
}
 
amount = INTVAL (rotate_amount);
 
/* 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;
rotate_type =
(rotate_type == SHIFT_ASHIFT) ? SHIFT_LSHIFTRT : SHIFT_ASHIFT;
}
 
/* 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))
{
switch (mode)
{
case 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:
/* This code works on the H8/300H and H8S. */
insn_buf = "xor.w\t%e0,%f0\n\txor.w\t%f0,%e0\n\txor.w\t%e0,%f0";
output_asm_insn (insn_buf, operands);
break;
 
default:
gcc_unreachable ();
}
 
/* Adjust AMOUNT and flip the direction. */
amount = GET_MODE_BITSIZE (mode) / 2 - amount;
rotate_type =
(rotate_type == SHIFT_ASHIFT) ? SHIFT_LSHIFTRT : SHIFT_ASHIFT;
}
 
/* Output rotate insns. */
for (bits = TARGET_H8300S ? 2 : 1; bits > 0; bits /= 2)
{
if (bits == 2)
insn_buf = rotate_two[rotate_type][rotate_mode];
else
insn_buf = rotate_one[cpu_type][rotate_type][rotate_mode];
 
for (; amount >= bits; amount -= bits)
output_asm_insn (insn_buf, operands);
}
 
return "";
}
 
/* 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;
 
gcc_assert (GET_CODE (amount_rtx) == CONST_INT);
 
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;
}
/* Fix the operands of a gen_xxx so that it could become a bit
operating insn. */
 
int
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 (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
&& !OK_FOR_U (operands[0]))
{
rtx mem = gen_rtx_MEM (GET_MODE (operands[0]),
copy_to_mode_reg (Pmode,
XEXP (operands[0], 0)));
MEM_COPY_ATTRIBUTES (mem, operands[0]);
operands[0] = mem;
}
 
if (GET_CODE (operands[1]) == MEM
&& !OK_FOR_U (operands[1]))
{
rtx mem = gen_rtx_MEM (GET_MODE (operands[1]),
copy_to_mode_reg (Pmode,
XEXP (operands[1], 0)));
MEM_COPY_ATTRIBUTES (mem, operands[0]);
operands[1] = mem;
}
return 0;
}
 
/* Dest and src op must be register. */
 
operands[1] = force_reg (QImode, operands[1]);
{
rtx res = gen_reg_rtx (QImode);
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:
gcc_unreachable ();
}
emit_insn (gen_movqi (operands[0], res));
}
return 1;
}
 
/* Return nonzero if FUNC is an interrupt function as specified
by the "interrupt" attribute. */
 
static int
h8300_interrupt_function_p (tree func)
{
tree a;
 
if (TREE_CODE (func) != FUNCTION_DECL)
return 0;
 
a = lookup_attribute ("interrupt_handler", DECL_ATTRIBUTES (func));
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 (tree func)
{
tree a;
 
if (TREE_CODE (func) != FUNCTION_DECL)
return 0;
 
a = lookup_attribute ("OS_Task", DECL_ATTRIBUTES (func));
return a != NULL_TREE;
}
 
/* Return nonzero if FUNC is a monitor function as specified
by the "monitor" attribute. */
 
static int
h8300_monitor_function_p (tree func)
{
tree a;
 
if (TREE_CODE (func) != FUNCTION_DECL)
return 0;
 
a = lookup_attribute ("monitor", DECL_ATTRIBUTES (func));
return a != NULL_TREE;
}
 
/* Return nonzero if FUNC is a function that should be called
through the function vector. */
 
int
h8300_funcvec_function_p (tree func)
{
tree a;
 
if (TREE_CODE (func) != FUNCTION_DECL)
return 0;
 
a = lookup_attribute ("function_vector", DECL_ATTRIBUTES (func));
return a != NULL_TREE;
}
 
/* Return nonzero if DECL is a variable that's in the eight bit
data area. */
 
int
h8300_eightbit_data_p (tree decl)
{
tree a;
 
if (TREE_CODE (decl) != VAR_DECL)
return 0;
 
a = lookup_attribute ("eightbit_data", DECL_ATTRIBUTES (decl));
return a != NULL_TREE;
}
 
/* Return nonzero if DECL is a variable that's in the tiny
data area. */
 
int
h8300_tiny_data_p (tree decl)
{
tree a;
 
if (TREE_CODE (decl) != VAR_DECL)
return 0;
 
a = lookup_attribute ("tiny_data", DECL_ATTRIBUTES (decl));
return a != NULL_TREE;
}
 
/* Generate an 'interrupt_handler' attribute for decls. We convert
all the pragmas to corresponding attributes. */
 
static void
h8300_insert_attributes (tree node, tree *attributes)
{
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);
}
 
if (pragma_saveall)
{
pragma_saveall = 0;
 
/* 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.
 
eightbit_data: This variable lives in the 8-bit data area and can
be referenced with 8-bit absolute memory addresses.
 
tiny_data: This variable lives in the tiny data area and can be
referenced with 16-bit absolute memory references. */
 
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 }
};
 
 
/* Handle an attribute requiring a FUNCTION_DECL; arguments as in
struct attribute_spec.handler. */
static tree
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 (OPT_Wattributes, "%qs attribute only applies to functions",
IDENTIFIER_POINTER (name));
*no_add_attrs = true;
}
 
return NULL_TREE;
}
 
/* Handle an "eightbit_data" attribute; arguments as in
struct attribute_spec.handler. */
static tree
h8300_handle_eightbit_data_attribute (tree *node, tree name,
tree args ATTRIBUTE_UNUSED,
int flags ATTRIBUTE_UNUSED,
bool *no_add_attrs)
{
tree decl = *node;
 
if (TREE_STATIC (decl) || DECL_EXTERNAL (decl))
{
DECL_SECTION_NAME (decl) = build_string (7, ".eight");
}
else
{
warning (OPT_Wattributes, "%qs attribute ignored",
IDENTIFIER_POINTER (name));
*no_add_attrs = true;
}
 
return NULL_TREE;
}
 
/* Handle an "tiny_data" attribute; arguments as in
struct attribute_spec.handler. */
static tree
h8300_handle_tiny_data_attribute (tree *node, tree name,
tree args ATTRIBUTE_UNUSED,
int flags ATTRIBUTE_UNUSED,
bool *no_add_attrs)
{
tree decl = *node;
 
if (TREE_STATIC (decl) || DECL_EXTERNAL (decl))
{
DECL_SECTION_NAME (decl) = build_string (6, ".tiny");
}
else
{
warning (OPT_Wattributes, "%qs attribute ignored",
IDENTIFIER_POINTER (name));
*no_add_attrs = true;
}
 
return NULL_TREE;
}
 
/* Mark function vectors, and various small data objects. */
 
static void
h8300_encode_section_info (tree decl, rtx rtl, int first)
{
int extra_flags = 0;
 
default_encode_section_info (decl, rtl, first);
 
if (TREE_CODE (decl) == FUNCTION_DECL
&& h8300_funcvec_function_p (decl))
extra_flags = SYMBOL_FLAG_FUNCVEC_FUNCTION;
else if (TREE_CODE (decl) == VAR_DECL
&& (TREE_STATIC (decl) || DECL_EXTERNAL (decl)))
{
if (h8300_eightbit_data_p (decl))
extra_flags = SYMBOL_FLAG_EIGHTBIT_DATA;
else if (first && h8300_tiny_data_p (decl))
extra_flags = SYMBOL_FLAG_TINY_DATA;
}
 
if (extra_flags)
SYMBOL_REF_FLAGS (XEXP (rtl, 0)) |= extra_flags;
}
 
/* Output a single-bit extraction. */
 
const char *
output_simode_bld (int bild, rtx operands[])
{
if (TARGET_H8300)
{
/* Clear the destination register. */
output_asm_insn ("sub.w\t%e0,%e0\n\tsub.w\t%f0,%f0", operands);
 
/* Now output the bit load or bit inverse load, and store it in
the destination. */
if (bild)
output_asm_insn ("bild\t%Z2,%Y1", operands);
else
output_asm_insn ("bld\t%Z2,%Y1", operands);
 
output_asm_insn ("bst\t#0,%w0", operands);
}
else
{
/* Determine if we can clear the destination first. */
int clear_first = (REG_P (operands[0]) && REG_P (operands[1])
&& REGNO (operands[0]) != REGNO (operands[1]));
 
if (clear_first)
output_asm_insn ("sub.l\t%S0,%S0", operands);
 
/* Output the bit load or bit inverse load. */
if (bild)
output_asm_insn ("bild\t%Z2,%Y1", operands);
else
output_asm_insn ("bld\t%Z2,%Y1", operands);
 
if (!clear_first)
output_asm_insn ("xor.l\t%S0,%S0", operands);
 
/* Perform the bit store. */
output_asm_insn ("rotxl.l\t%S0", operands);
}
 
/* All done. */
return "";
}
 
/* 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. */
 
static void
h8300_reorg (void)
{
if (flag_delayed_branch)
shorten_branches (get_insns ());
}
 
#ifndef OBJECT_FORMAT_ELF
static void
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);
}
#endif /* ! OBJECT_FORMAT_ELF */
 
/* Nonzero if X is a constant address suitable as an 8-bit absolute,
which is a special case of the 'R' operand. */
 
int
h8300_eightbit_constant_address_p (rtx x)
{
/* The ranges of the 8-bit area. */
const unsigned HOST_WIDE_INT n1 = trunc_int_for_mode (0xff00, HImode);
const unsigned HOST_WIDE_INT n2 = trunc_int_for_mode (0xffff, HImode);
const unsigned HOST_WIDE_INT h1 = trunc_int_for_mode (0x00ffff00, SImode);
const unsigned HOST_WIDE_INT h2 = trunc_int_for_mode (0x00ffffff, SImode);
const unsigned HOST_WIDE_INT s1 = trunc_int_for_mode (0xffffff00, SImode);
const unsigned HOST_WIDE_INT s2 = trunc_int_for_mode (0xffffffff, SImode);
 
unsigned HOST_WIDE_INT addr;
 
/* We accept symbols declared with eightbit_data. */
if (GET_CODE (x) == SYMBOL_REF)
return (SYMBOL_REF_FLAGS (x) & SYMBOL_FLAG_EIGHTBIT_DATA) != 0;
 
if (GET_CODE (x) != CONST_INT)
return 0;
 
addr = INTVAL (x);
 
return (0
|| ((TARGET_H8300 || TARGET_NORMAL_MODE) && IN_RANGE (addr, n1, n2))
|| (TARGET_H8300H && IN_RANGE (addr, h1, h2))
|| (TARGET_H8300S && IN_RANGE (addr, s1, s2)));
}
 
/* Nonzero if X is a constant address suitable as an 16-bit absolute
on H8/300H and H8S. */
 
int
h8300_tiny_constant_address_p (rtx x)
{
/* The ranges of the 16-bit area. */
const unsigned HOST_WIDE_INT h1 = trunc_int_for_mode (0x00000000, SImode);
const unsigned HOST_WIDE_INT h2 = trunc_int_for_mode (0x00007fff, SImode);
const unsigned HOST_WIDE_INT h3 = trunc_int_for_mode (0x00ff8000, SImode);
const unsigned HOST_WIDE_INT h4 = trunc_int_for_mode (0x00ffffff, SImode);
const unsigned HOST_WIDE_INT s1 = trunc_int_for_mode (0x00000000, SImode);
const unsigned HOST_WIDE_INT s2 = trunc_int_for_mode (0x00007fff, SImode);
const unsigned HOST_WIDE_INT s3 = trunc_int_for_mode (0xffff8000, SImode);
const unsigned HOST_WIDE_INT s4 = trunc_int_for_mode (0xffffffff, SImode);
 
unsigned HOST_WIDE_INT addr;
 
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);
 
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))));
 
case CONST:
return TARGET_NORMAL_MODE;
 
default:
return 0;
}
 
}
 
/* 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 (rtx addr1, rtx addr2)
{
HOST_WIDE_INT offset1, offset2;
rtx reg1, reg2;
 
if (REG_P (addr1))
{
reg1 = addr1;
offset1 = 0;
}
else if (GET_CODE (addr1) == PLUS
&& REG_P (XEXP (addr1, 0))
&& GET_CODE (XEXP (addr1, 1)) == CONST_INT)
{
reg1 = XEXP (addr1, 0);
offset1 = INTVAL (XEXP (addr1, 1));
}
else
return 0;
 
if (REG_P (addr2))
{
reg2 = addr2;
offset2 = 0;
}
else if (GET_CODE (addr2) == PLUS
&& REG_P (XEXP (addr2, 0))
&& GET_CODE (XEXP (addr2, 1)) == CONST_INT)
{
reg2 = XEXP (addr2, 0);
offset2 = INTVAL (XEXP (addr2, 1));
}
else
return 0;
 
if (((reg1 == stack_pointer_rtx && reg2 == stack_pointer_rtx)
|| (reg1 == frame_pointer_rtx && reg2 == frame_pointer_rtx))
&& offset1 % 2 == 0
&& offset1 + 1 == offset2)
return 1;
 
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);
default:
gcc_unreachable ();
}
}
 
/* 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;
}
/* 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");
}
/* 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));
}
/* 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
 
#undef TARGET_DEFAULT_TARGET_FLAGS
#define TARGET_DEFAULT_TARGET_FLAGS TARGET_DEFAULT
 
struct gcc_target targetm = TARGET_INITIALIZER;
/ctzhi2.c
0,0 → 1,40
/* The implementation of __ctzhi2.
Copyright (C) 2003 Free Software Foundation, Inc.
 
This file is part of GCC.
 
GCC is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
 
In addition to the permissions in the GNU General Public License, the
Free Software Foundation gives you unlimited permission to link the
compiled version of this file into combinations with other programs,
and to distribute those combinations without any restriction coming
from the use of this file. (The General Public License restrictions
do apply in other respects; for example, they cover modification of
the file, and distribution when not linked into a combine
executable.)
 
GCC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
 
You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING. If not, write to
the Free Software Foundation, 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA. */
 
int __ctzhi2 (unsigned short x);
 
int
__ctzhi2 (unsigned short x)
{
int i;
for (i = 0; i < 16; i++)
if (x & ((unsigned short) 1 << i))
break;
return i;
}
/crtn.asm
0,0 → 1,58
/* Copyright (C) 2001 Free Software Foundation, Inc.
This file was adapted from glibc sources.
 
This file is part of GCC.
 
GCC is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the
Free Software Foundation; either version 2, or (at your option) any
later version.
 
In addition to the permissions in the GNU General Public License, the
Free Software Foundation gives you unlimited permission to link the
compiled version of this file into combinations with other programs,
and to distribute those combinations without any restriction coming
from the use of this file. (The General Public License restrictions
do apply in other respects; for example, they cover modification of
the file, and distribution when not linked into a combine
executable.)
 
GCC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
 
You should have received a copy of the GNU General Public License
along with this program; see the file COPYING. If not, write to
the Free Software Foundation, 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA. */
 
/* See an explanation about .init and .fini in crti.asm. */
 
#ifdef __H8300H__
#ifdef __NORMAL_MODE__
.h8300hn
#else
.h8300h
#endif
#endif
 
#ifdef __H8300S__
#ifdef __NORMAL_MODE__
.h8300sn
#else
.h8300s
#endif
#endif
#ifdef __H8300SX__
#ifdef __NORMAL_MODE__
.h8300sxn
#else
.h8300sx
#endif
#endif
.section .init
rts
 
.section .fini
rts
/h8300.h
0,0 → 1,1207
/* Definitions of target machine for GNU compiler.
Renesas H8/300 (generic)
Copyright (C) 1992, 1993, 1994, 1995, 1996, 1996, 1997, 1998, 1999,
2000, 2001, 2002, 2003, 2004, 2005, 2007 Free Software Foundation, Inc.
Contributed by Steve Chamberlain (sac@cygnus.com),
Jim Wilson (wilson@cygnus.com), and Doug Evans (dje@cygnus.com).
 
This file is part of GCC.
 
GCC is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3, or (at your option)
any later version.
 
GCC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
 
You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING3. If not see
<http://www.gnu.org/licenses/>. */
 
#ifndef GCC_H8300_H
#define GCC_H8300_H
 
/* Which CPU to compile for.
We use int for CPU_TYPE to avoid lots of casts. */
#if 0 /* defined in insn-attr.h, here for documentation */
enum attr_cpu { CPU_H8300, CPU_H8300H };
#endif
extern int cpu_type;
 
/* Various globals defined in h8300.c. */
 
extern const char *h8_push_op, *h8_pop_op, *h8_mov_op;
extern const char * const *h8_reg_names;
 
/* Target CPU builtins. */
#define TARGET_CPU_CPP_BUILTINS() \
do \
{ \
if (TARGET_H8300H) \
{ \
builtin_define ("__H8300H__"); \
builtin_assert ("cpu=h8300h"); \
builtin_assert ("machine=h8300h"); \
if (TARGET_NORMAL_MODE) \
{ \
builtin_define ("__NORMAL_MODE__"); \
} \
} \
else if (TARGET_H8300SX) \
{ \
builtin_define ("__H8300SX__"); \
if (TARGET_NORMAL_MODE) \
{ \
builtin_define ("__NORMAL_MODE__"); \
} \
} \
else if (TARGET_H8300S) \
{ \
builtin_define ("__H8300S__"); \
builtin_assert ("cpu=h8300s"); \
builtin_assert ("machine=h8300s"); \
if (TARGET_NORMAL_MODE) \
{ \
builtin_define ("__NORMAL_MODE__"); \
} \
} \
else \
{ \
builtin_define ("__H8300__"); \
builtin_assert ("cpu=h8300"); \
builtin_assert ("machine=h8300"); \
} \
} \
while (0)
 
#define LINK_SPEC "%{mh:%{mn:-m h8300hn}} %{mh:%{!mn:-m h8300h}} %{ms:%{mn:-m h8300sn}} %{ms:%{!mn:-m h8300s}}"
 
#define LIB_SPEC "%{mrelax:-relax} %{g:-lg} %{!p:%{!pg:-lc}}%{p:-lc_p}%{pg:-lc_p}"
 
#define OPTIMIZATION_OPTIONS(LEVEL, SIZE) \
do \
{ \
/* Basic block reordering is only beneficial on targets with cache \
and/or variable-cycle branches where (cycle count taken != \
cycle count not taken). */ \
flag_reorder_blocks = 0; \
} \
while (0)
 
/* Print subsidiary information on the compiler version in use. */
 
#define TARGET_VERSION fprintf (stderr, " (Renesas H8/300)");
 
/* Macros used in the machine description to test the flags. */
 
/* Select between the H8/300 and H8/300H CPUs. */
#define TARGET_H8300 (! TARGET_H8300H && ! TARGET_H8300S)
#define TARGET_H8300S (TARGET_H8300S_1 || TARGET_H8300SX)
/* Some multiply instructions are not available in all H8SX variants.
Use this macro instead of TARGET_H8300SX to indicate this, even
though we don't actually generate different code for now. */
#define TARGET_H8300SXMUL TARGET_H8300SX
 
#ifdef IN_LIBGCC2
#undef TARGET_H8300H
#undef TARGET_H8300S
#undef TARGET_NORMAL_MODE
/* If compiling libgcc2, make these compile time constants based on what
flags are we actually compiling with. */
#ifdef __H8300H__
#define TARGET_H8300H 1
#else
#define TARGET_H8300H 0
#endif
#ifdef __H8300S__
#define TARGET_H8300S 1
#else
#define TARGET_H8300S 0
#endif
#ifdef __NORMAL_MODE__
#define TARGET_NORMAL_MODE 1
#else
#define TARGET_NORMAL_MODE 0
#endif
#endif /* !IN_LIBGCC2 */
 
/* Do things that must be done once at start up. */
 
#define OVERRIDE_OPTIONS \
do \
{ \
h8300_init_once (); \
} \
while (0)
 
/* Default target_flags if no switches specified. */
 
#ifndef TARGET_DEFAULT
#define TARGET_DEFAULT (MASK_QUICKCALL)
#endif
 
/* Show we can debug even without a frame pointer. */
/* #define CAN_DEBUG_WITHOUT_FP */
 
/* Define this if addresses of constant functions
shouldn't be put through pseudo regs where they can be cse'd.
Desirable on machines where ordinary constants are expensive
but a CALL with constant address is cheap.
 
Calls through a register are cheaper than calls to named
functions; however, the register pressure this causes makes
CSEing of function addresses generally a lose. */
#define NO_FUNCTION_CSE
/* Target machine storage layout */
 
/* Define this if most significant bit is lowest numbered
in instructions that operate on numbered bit-fields.
This is not true on the H8/300. */
#define BITS_BIG_ENDIAN 0
 
/* Define this if most significant byte of a word is the lowest numbered. */
/* That is true on the H8/300. */
#define BYTES_BIG_ENDIAN 1
 
/* Define this if most significant word of a multiword number is lowest
numbered. */
#define WORDS_BIG_ENDIAN 1
 
#define MAX_BITS_PER_WORD 32
 
/* Width of a word, in units (bytes). */
#define UNITS_PER_WORD (TARGET_H8300H || TARGET_H8300S ? 4 : 2)
#define MIN_UNITS_PER_WORD 2
 
#define SHORT_TYPE_SIZE 16
#define INT_TYPE_SIZE (TARGET_INT32 ? 32 : 16)
#define LONG_TYPE_SIZE 32
#define LONG_LONG_TYPE_SIZE 64
#define FLOAT_TYPE_SIZE 32
#define DOUBLE_TYPE_SIZE 32
#define LONG_DOUBLE_TYPE_SIZE DOUBLE_TYPE_SIZE
 
#define MAX_FIXED_MODE_SIZE 32
 
/* Allocation boundary (in *bits*) for storing arguments in argument list. */
#define PARM_BOUNDARY (TARGET_H8300H || TARGET_H8300S ? 32 : 16)
 
/* Allocation boundary (in *bits*) for the code of a function. */
#define FUNCTION_BOUNDARY 16
 
/* Alignment of field after `int : 0' in a structure. */
/* One can argue this should be 32 for -mint32, but since 32 bit ints only
need 16 bit alignment, this is left as is so that -mint32 doesn't change
structure layouts. */
#define EMPTY_FIELD_BOUNDARY 16
 
/* No data type wants to be aligned rounder than this.
32 bit values are aligned as such on the H8/300H and H8S for speed. */
#define BIGGEST_ALIGNMENT \
(((TARGET_H8300H || TARGET_H8300S) && ! TARGET_ALIGN_300) ? 32 : 16)
 
/* The stack goes in 16/32 bit lumps. */
#define STACK_BOUNDARY (TARGET_H8300 ? 16 : 32)
 
/* Define this if move instructions will actually fail to work
when given unaligned data. */
/* On the H8/300, longs can be aligned on halfword boundaries, but not
byte boundaries. */
#define STRICT_ALIGNMENT 1
/* Standard register usage. */
 
/* Number of actual hardware registers.
The hardware registers are assigned numbers for the compiler
from 0 to just below FIRST_PSEUDO_REGISTER.
 
All registers that the compiler knows about must be given numbers,
even those that are not normally considered general registers.
 
Reg 9 does not correspond to any hardware register, but instead
appears in the RTL as an argument pointer prior to reload, and is
eliminated during reloading in favor of either the stack or frame
pointer. */
 
#define FIRST_PSEUDO_REGISTER 12
 
/* 1 for registers that have pervasive standard uses
and are not available for the register allocator. */
 
#define FIXED_REGISTERS \
/* r0 r1 r2 r3 r4 r5 r6 r7 mac ap rap fp */ \
{ 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1 }
 
/* 1 for registers not available across function calls.
These must include the FIXED_REGISTERS and also any
registers that can be used without being saved.
The latter must include the registers where values are returned
and the register where structure-value addresses are passed.
Aside from that, you can include as many other registers as you
like.
 
H8 destroys r0,r1,r2,r3. */
 
#define CALL_USED_REGISTERS \
/* r0 r1 r2 r3 r4 r5 r6 r7 mac ap rap fp */ \
{ 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1 }
 
#define REG_ALLOC_ORDER \
/* r0 r1 r2 r3 r4 r5 r6 r7 mac ap rap fp */ \
{ 2, 3, 0, 1, 4, 5, 6, 8, 7, 9, 10, 11 }
 
#define CONDITIONAL_REGISTER_USAGE \
{ \
if (!TARGET_MAC) \
fixed_regs[MAC_REG] = call_used_regs[MAC_REG] = 1; \
}
 
#define HARD_REGNO_NREGS(REGNO, MODE) \
h8300_hard_regno_nregs ((REGNO), (MODE))
 
#define HARD_REGNO_MODE_OK(REGNO, MODE) \
h8300_hard_regno_mode_ok ((REGNO), (MODE))
 
/* Value is 1 if it is a good idea to tie two pseudo registers
when one has mode MODE1 and one has mode MODE2.
If HARD_REGNO_MODE_OK could produce different values for MODE1 and MODE2,
for any hard reg, then this must be 0 for correct output. */
#define MODES_TIEABLE_P(MODE1, MODE2) \
((MODE1) == (MODE2) \
|| (((MODE1) == QImode || (MODE1) == HImode \
|| ((TARGET_H8300H || TARGET_H8300S) && (MODE1) == SImode)) \
&& ((MODE2) == QImode || (MODE2) == HImode \
|| ((TARGET_H8300H || TARGET_H8300S) && (MODE2) == SImode))))
 
/* A C expression that is nonzero if hard register NEW_REG can be
considered for use as a rename register for OLD_REG register */
 
#define HARD_REGNO_RENAME_OK(OLD_REG, NEW_REG) \
h8300_hard_regno_rename_ok (OLD_REG, NEW_REG)
 
/* Specify the registers used for certain standard purposes.
The values of these macros are register numbers. */
 
/* H8/300 pc is not overloaded on a register. */
 
/*#define PC_REGNUM 15*/
 
/* Register to use for pushing function arguments. */
#define STACK_POINTER_REGNUM SP_REG
 
/* Base register for access to local variables of the function. */
#define HARD_FRAME_POINTER_REGNUM HFP_REG
 
/* Base register for access to local variables of the function. */
#define FRAME_POINTER_REGNUM FP_REG
 
/* Value should be nonzero if functions must have frame pointers.
Zero means the frame pointer need not be set up (and parms
may be accessed via the stack pointer) in functions that seem suitable.
This is computed in `reload', in reload1.c. */
#define FRAME_POINTER_REQUIRED 0
 
/* Base register for access to arguments of the function. */
#define ARG_POINTER_REGNUM AP_REG
 
/* Register in which static-chain is passed to a function. */
#define STATIC_CHAIN_REGNUM SC_REG
 
/* Fake register that holds the address on the stack of the
current function's return address. */
#define RETURN_ADDRESS_POINTER_REGNUM RAP_REG
 
/* A C expression whose value is RTL representing the value of the return
address for the frame COUNT steps up from the current frame.
FRAMEADDR is already the frame pointer of the COUNT frame, assuming
a stack layout with the frame pointer as the first saved register. */
#define RETURN_ADDR_RTX(COUNT, FRAME) h8300_return_addr_rtx ((COUNT), (FRAME))
/* Define the classes of registers for register constraints in the
machine description. Also define ranges of constants.
 
One of the classes must always be named ALL_REGS and include all hard regs.
If there is more than one class, another class must be named NO_REGS
and contain no registers.
 
The name GENERAL_REGS must be the name of a class (or an alias for
another name such as ALL_REGS). This is the class of registers
that is allowed by "g" or "r" in a register constraint.
Also, registers outside this class are allocated only when
instructions express preferences for them.
 
The classes must be numbered in nondecreasing order; that is,
a larger-numbered class must never be contained completely
in a smaller-numbered class.
 
For any two classes, it is very desirable that there be another
class that represents their union. */
 
enum reg_class {
NO_REGS, COUNTER_REGS, SOURCE_REGS, DESTINATION_REGS,
GENERAL_REGS, MAC_REGS, ALL_REGS, LIM_REG_CLASSES
};
 
#define N_REG_CLASSES ((int) LIM_REG_CLASSES)
 
/* Give names of register classes as strings for dump file. */
 
#define REG_CLASS_NAMES \
{ "NO_REGS", "COUNTER_REGS", "SOURCE_REGS", "DESTINATION_REGS", \
"GENERAL_REGS", "MAC_REGS", "ALL_REGS", "LIM_REGS" }
 
/* Define which registers fit in which classes.
This is an initializer for a vector of HARD_REG_SET
of length N_REG_CLASSES. */
 
#define REG_CLASS_CONTENTS \
{ {0}, /* No regs */ \
{0x010}, /* COUNTER_REGS */ \
{0x020}, /* SOURCE_REGS */ \
{0x040}, /* DESTINATION_REGS */ \
{0xeff}, /* GENERAL_REGS */ \
{0x100}, /* MAC_REGS */ \
{0xfff}, /* ALL_REGS */ \
}
 
/* The same information, inverted:
Return the class number of the smallest class containing
reg number REGNO. This could be a conditional expression
or could index an array. */
 
#define REGNO_REG_CLASS(REGNO) \
((REGNO) == MAC_REG ? MAC_REGS \
: (REGNO) == COUNTER_REG ? COUNTER_REGS \
: (REGNO) == SOURCE_REG ? SOURCE_REGS \
: (REGNO) == DESTINATION_REG ? DESTINATION_REGS \
: GENERAL_REGS)
 
/* The class value for index registers, and the one for base regs. */
 
#define INDEX_REG_CLASS (TARGET_H8300SX ? GENERAL_REGS : NO_REGS)
#define BASE_REG_CLASS GENERAL_REGS
 
/* Get reg_class from a letter such as appears in the machine description.
 
'a' is the MAC register. */
 
#define REG_CLASS_FROM_LETTER(C) (h8300_reg_class_from_letter (C))
 
/* The letters I, J, K, L, M, N, O, P in a register constraint string
can be used to stand for particular ranges of immediate operands.
This macro defines what the ranges are.
C is the letter, and VALUE is a constant value.
Return 1 if VALUE is in the range specified by C. */
 
#define CONST_OK_FOR_I(VALUE) ((VALUE) == 0)
#define CONST_OK_FOR_J(VALUE) (((VALUE) & 0xff) == 0)
#define CONST_OK_FOR_L(VALUE) \
(TARGET_H8300H || TARGET_H8300S \
? (VALUE) == 1 || (VALUE) == 2 || (VALUE) == 4 \
: (VALUE) == 1 || (VALUE) == 2)
#define CONST_OK_FOR_M(VALUE) \
((VALUE) == 1 || (VALUE) == 2)
#define CONST_OK_FOR_N(VALUE) \
(TARGET_H8300H || TARGET_H8300S \
? (VALUE) == -1 || (VALUE) == -2 || (VALUE) == -4 \
: (VALUE) == -1 || (VALUE) == -2)
#define CONST_OK_FOR_O(VALUE) \
((VALUE) == -1 || (VALUE) == -2)
 
/* Multi-letter constraints for constant are always started with P
(just because it was the only letter in the range left. New
constraints for constants should be added here. */
#define CONST_OK_FOR_Ppositive(VALUE, NBITS) \
((VALUE) > 0 && (VALUE) < (1 << (NBITS)))
#define CONST_OK_FOR_Pnegative(VALUE, NBITS) \
((VALUE) < 0 && (VALUE) > -(1 << (NBITS)))
#define CONST_OK_FOR_P(VALUE, STR) \
((STR)[1] >= '1' && (STR)[1] <= '9' && (STR)[2] == '<' \
? (((STR)[3] == '0' || ((STR)[3] == 'X' && TARGET_H8300SX)) \
&& CONST_OK_FOR_Pnegative ((VALUE), (STR)[1] - '0')) \
: ((STR)[1] >= '1' && (STR)[1] <= '9' && (STR)[2] == '>') \
? (((STR)[3] == '0' || ((STR)[3] == 'X' && TARGET_H8300SX)) \
&& CONST_OK_FOR_Ppositive ((VALUE), (STR)[1] - '0')) \
: 0)
#define CONSTRAINT_LEN_FOR_P(STR) \
((((STR)[1] >= '1' && (STR)[1] <= '9') \
&& ((STR)[2] == '<' || (STR)[2] == '>') \
&& ((STR)[3] == 'X' || (STR)[3] == '0')) ? 4 \
: 0)
 
#define CONST_OK_FOR_CONSTRAINT_P(VALUE, C, STR) \
((C) == 'P' ? CONST_OK_FOR_P ((VALUE), (STR)) \
: CONST_OK_FOR_LETTER_P ((VALUE), (C)))
#define CONST_OK_FOR_LETTER_P(VALUE, C) \
((C) == 'I' ? CONST_OK_FOR_I (VALUE) : \
(C) == 'J' ? CONST_OK_FOR_J (VALUE) : \
(C) == 'L' ? CONST_OK_FOR_L (VALUE) : \
(C) == 'M' ? CONST_OK_FOR_M (VALUE) : \
(C) == 'N' ? CONST_OK_FOR_N (VALUE) : \
(C) == 'O' ? CONST_OK_FOR_O (VALUE) : \
0)
 
/* Similar, but for floating constants, and defining letters G and H.
Here VALUE is the CONST_DOUBLE rtx itself.
 
`G' is a floating-point zero. */
 
#define CONST_DOUBLE_OK_FOR_LETTER_P(VALUE, C) \
((C) == 'G' ? (VALUE) == CONST0_RTX (SFmode) \
: 0)
 
/* Given an rtx X being reloaded into a reg required to be
in class CLASS, return the class of reg to actually use.
In general this is just CLASS; but on some machines
in some cases it is preferable to use a more restrictive class. */
 
#define PREFERRED_RELOAD_CLASS(X, CLASS) (CLASS)
 
/* Return the maximum number of consecutive registers
needed to represent mode MODE in a register of class CLASS. */
 
/* On the H8, this is the size of MODE in words. */
 
#define CLASS_MAX_NREGS(CLASS, MODE) \
((GET_MODE_SIZE (MODE) + UNITS_PER_WORD - 1) / UNITS_PER_WORD)
 
/* Any SI register-to-register move may need to be reloaded,
so define REGISTER_MOVE_COST to be > 2 so that reload never
shortcuts. */
 
#define REGISTER_MOVE_COST(MODE, CLASS1, CLASS2) \
(CLASS1 == MAC_REGS || CLASS2 == MAC_REGS ? 6 : 3)
/* Stack layout; function entry, exit and calling. */
 
/* Define this if pushing a word on the stack
makes the stack pointer a smaller address. */
 
#define STACK_GROWS_DOWNWARD
 
/* Define this to nonzero if the nominal address of the stack frame
is at the high-address end of the local variables;
that is, each additional local variable allocated
goes at a more negative offset in the frame. */
 
#define FRAME_GROWS_DOWNWARD 1
 
/* Offset within stack frame to start allocating local variables at.
If FRAME_GROWS_DOWNWARD, this is the offset to the END of the
first local allocated. Otherwise, it is the offset to the BEGINNING
of the first local allocated. */
 
#define STARTING_FRAME_OFFSET 0
 
/* If we generate an insn to push BYTES bytes,
this says how many the stack pointer really advances by.
 
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. */
 
#define PUSH_ROUNDING(BYTES) \
(((BYTES) + PARM_BOUNDARY / 8 - 1) & -PARM_BOUNDARY / 8)
 
/* Offset of first parameter from the argument pointer register value. */
/* Is equal to the size of the saved fp + pc, even if an fp isn't
saved since the value is used before we know. */
 
#define FIRST_PARM_OFFSET(FNDECL) 0
 
/* Value is the number of bytes of arguments automatically
popped when returning from a subroutine call.
FUNDECL is the declaration node of the function (as a tree),
FUNTYPE is the data type of the function (as a tree),
or for a library call it is an identifier node for the subroutine name.
SIZE is the number of bytes of arguments passed on the stack.
 
On the H8 the return does not pop anything. */
 
#define RETURN_POPS_ARGS(FUNDECL, FUNTYPE, SIZE) 0
 
/* Definitions for register eliminations.
 
This is an array of structures. Each structure initializes one pair
of eliminable registers. The "from" register number is given first,
followed by "to". Eliminations of the same "from" register are listed
in order of preference.
 
We have three registers that can be eliminated on the h8300.
First, the frame pointer register can often be eliminated in favor
of the stack pointer register. Secondly, the argument pointer
register and the return address pointer register are always
eliminated; they are replaced with either the stack or frame
pointer. */
 
#define ELIMINABLE_REGS \
{{ ARG_POINTER_REGNUM, STACK_POINTER_REGNUM}, \
{ ARG_POINTER_REGNUM, HARD_FRAME_POINTER_REGNUM}, \
{ RETURN_ADDRESS_POINTER_REGNUM, STACK_POINTER_REGNUM}, \
{ RETURN_ADDRESS_POINTER_REGNUM, HARD_FRAME_POINTER_REGNUM}, \
{ FRAME_POINTER_REGNUM, STACK_POINTER_REGNUM}, \
{ FRAME_POINTER_REGNUM, HARD_FRAME_POINTER_REGNUM}}
 
/* Given FROM and TO register numbers, say whether this elimination is allowed.
Frame pointer elimination is automatically handled.
 
For the h8300, if frame pointer elimination is being done, we would like to
convert ap and rp into sp, not fp.
 
All other eliminations are valid. */
 
#define CAN_ELIMINATE(FROM, TO) \
((TO) == STACK_POINTER_REGNUM ? ! frame_pointer_needed : 1)
 
/* Define the offset between two registers, one to be eliminated, and the other
its replacement, at the start of a routine. */
 
#define INITIAL_ELIMINATION_OFFSET(FROM, TO, OFFSET) \
((OFFSET) = h8300_initial_elimination_offset ((FROM), (TO)))
 
/* Define how to find the value returned by a function.
VALTYPE is the data type of the value (as a tree).
If the precise function being called is known, FUNC is its FUNCTION_DECL;
otherwise, FUNC is 0.
 
On the H8 the return value is in R0/R1. */
 
#define FUNCTION_VALUE(VALTYPE, FUNC) \
gen_rtx_REG (TYPE_MODE (VALTYPE), R0_REG)
 
/* Define how to find the value returned by a library function
assuming the value has mode MODE. */
 
/* On the H8 the return value is in R0/R1. */
 
#define LIBCALL_VALUE(MODE) \
gen_rtx_REG (MODE, R0_REG)
 
/* 1 if N is a possible register number for a function value.
On the H8, R0 is the only register thus used. */
 
#define FUNCTION_VALUE_REGNO_P(N) ((N) == R0_REG)
 
/* Define this if PCC uses the nonreentrant convention for returning
structure and union values. */
 
/*#define PCC_STATIC_STRUCT_RETURN*/
 
/* 1 if N is a possible register number for function argument passing.
On the H8, no registers are used in this way. */
 
#define FUNCTION_ARG_REGNO_P(N) (TARGET_QUICKCALL ? N < 3 : 0)
 
/* When defined, the compiler allows registers explicitly used in the
rtl to be used as spill registers but prevents the compiler from
extending the lifetime of these registers. */
 
#define SMALL_REGISTER_CLASSES 1
/* Define a data type for recording info about an argument list
during the scan of that argument list. This data type should
hold all necessary information about the function itself
and about the args processed so far, enough to enable macros
such as FUNCTION_ARG to determine where the next arg should go.
 
On the H8/300, this is a two item struct, the first is the number
of bytes scanned so far and the second is the rtx of the called
library function if any. */
 
#define CUMULATIVE_ARGS struct cum_arg
struct cum_arg
{
int nbytes;
struct rtx_def *libcall;
};
 
/* Initialize a variable CUM of type CUMULATIVE_ARGS
for a call to a function whose data type is FNTYPE.
For a library call, FNTYPE is 0.
 
On the H8/300, the offset starts at 0. */
 
#define INIT_CUMULATIVE_ARGS(CUM, FNTYPE, LIBNAME, INDIRECT, N_NAMED_ARGS) \
((CUM).nbytes = 0, (CUM).libcall = LIBNAME)
 
/* Update the data in CUM to advance over an argument
of mode MODE and data type TYPE.
(TYPE is null for libcalls where that information may not be available.) */
 
#define FUNCTION_ARG_ADVANCE(CUM, MODE, TYPE, NAMED) \
((CUM).nbytes += ((MODE) != BLKmode \
? (GET_MODE_SIZE (MODE) + UNITS_PER_WORD - 1) & -UNITS_PER_WORD \
: (int_size_in_bytes (TYPE) + UNITS_PER_WORD - 1) & -UNITS_PER_WORD))
 
/* Define where to put the arguments to a function.
Value is zero to push the argument on the stack,
or a hard register in which to store the argument.
 
MODE is the argument's machine mode.
TYPE is the data type of the argument (as a tree).
This is null for libcalls where that information may
not be available.
CUM is a variable of type CUMULATIVE_ARGS which gives info about
the preceding args and about the function being called.
NAMED is nonzero if this argument is a named parameter
(otherwise it is an extra parameter matching an ellipsis). */
 
/* On the H8/300 all normal args are pushed, unless -mquickcall in which
case the first 3 arguments are passed in registers.
See function `function_arg'. */
 
#define FUNCTION_ARG(CUM, MODE, TYPE, NAMED) \
function_arg (&CUM, MODE, TYPE, NAMED)
 
/* Output assembler code to FILE to increment profiler label # LABELNO
for profiling a function entry. */
 
#define FUNCTION_PROFILER(FILE, LABELNO) \
fprintf (FILE, "\t%s\t#LP%d,%s\n\tjsr @mcount\n", \
h8_mov_op, (LABELNO), h8_reg_names[0]);
 
/* EXIT_IGNORE_STACK should be nonzero if, when returning from a function,
the stack pointer does not matter. The value is tested only in
functions that have frame pointers.
No definition is equivalent to always zero. */
 
#define EXIT_IGNORE_STACK 0
 
/* We emit the entire trampoline with INITIALIZE_TRAMPOLINE.
Depending on the pointer size, we use a different trampoline.
 
Pmode == HImode
vvvv context
1 0000 7903xxxx mov.w #0x1234,r3
2 0004 5A00xxxx jmp @0x1234
^^^^ function
 
Pmode == SImode
vvvvvvvv context
2 0000 7A03xxxxxxxx mov.l #0x12345678,er3
3 0006 5Axxxxxx jmp @0x123456
^^^^^^ function
*/
 
/* Length in units of the trampoline for entering a nested function. */
 
#define TRAMPOLINE_SIZE ((Pmode == HImode) ? 8 : 12)
 
/* Emit RTL insns to build a trampoline.
FNADDR is an RTX for the address of the function's pure code.
CXT is an RTX for the static chain value for the function. */
 
#define INITIALIZE_TRAMPOLINE(TRAMP, FNADDR, CXT) \
do \
{ \
if (Pmode == HImode) \
{ \
emit_move_insn (gen_rtx_MEM (HImode, (TRAMP)), GEN_INT (0x7903)); \
emit_move_insn (gen_rtx_MEM (Pmode, plus_constant ((TRAMP), 2)), \
(CXT)); \
emit_move_insn (gen_rtx_MEM (Pmode, plus_constant ((TRAMP), 4)), \
GEN_INT (0x5a00)); \
emit_move_insn (gen_rtx_MEM (Pmode, plus_constant ((TRAMP), 6)), \
(FNADDR)); \
} \
else \
{ \
rtx tem = gen_reg_rtx (Pmode); \
\
emit_move_insn (gen_rtx_MEM (HImode, (TRAMP)), GEN_INT (0x7a03)); \
emit_move_insn (gen_rtx_MEM (Pmode, plus_constant ((TRAMP), 2)), \
(CXT)); \
emit_move_insn (tem, (FNADDR)); \
emit_insn (gen_andsi3 (tem, tem, GEN_INT (0x00ffffff))); \
emit_insn (gen_iorsi3 (tem, tem, GEN_INT (0x5a000000))); \
emit_move_insn (gen_rtx_MEM (Pmode, plus_constant ((TRAMP), 6)), \
tem); \
} \
} \
while (0)
/* Addressing modes, and classification of registers for them. */
 
#define HAVE_POST_INCREMENT 1
#define HAVE_PRE_DECREMENT 1
#define HAVE_POST_DECREMENT TARGET_H8300SX
#define HAVE_PRE_INCREMENT TARGET_H8300SX
 
/* Macros to check register numbers against specific register classes. */
 
/* These assume that REGNO is a hard or pseudo reg number.
They give nonzero only if REGNO is a hard reg of the suitable class
or a pseudo reg currently allocated to a suitable hard reg.
Since they use reg_renumber, they are safe only once reg_renumber
has been allocated, which happens in local-alloc.c. */
 
#define REGNO_OK_FOR_INDEX_P(regno) 0
 
#define REGNO_OK_FOR_BASE_P(regno) \
(((regno) < FIRST_PSEUDO_REGISTER && regno != MAC_REG) \
|| reg_renumber[regno] >= 0)
/* Maximum number of registers that can appear in a valid memory address. */
 
#define MAX_REGS_PER_ADDRESS 1
 
/* 1 if X is an rtx for a constant that is a valid address. */
 
#define CONSTANT_ADDRESS_P(X) \
(GET_CODE (X) == LABEL_REF || GET_CODE (X) == SYMBOL_REF \
|| (GET_CODE (X) == CONST_INT \
/* We handle signed and unsigned offsets here. */ \
&& INTVAL (X) > (TARGET_H8300 ? -0x10000 : -0x1000000) \
&& INTVAL (X) < (TARGET_H8300 ? 0x10000 : 0x1000000)) \
|| (GET_CODE (X) == HIGH || GET_CODE (X) == CONST))
 
/* Nonzero if the constant value X is a legitimate general operand.
It is given that X satisfies CONSTANT_P or is a CONST_DOUBLE. */
 
#define LEGITIMATE_CONSTANT_P(X) (h8300_legitimate_constant_p (X))
 
/* The macros REG_OK_FOR..._P assume that the arg is a REG rtx
and check its validity for a certain class.
We have two alternate definitions for each of them.
The usual definition accepts all pseudo regs; the other rejects
them unless they have been allocated suitable hard regs.
The symbol REG_OK_STRICT causes the latter definition to be used.
 
Most source files want to accept pseudo regs in the hope that
they will get allocated to the class that the insn wants them to be in.
Source files for reload pass need to be strict.
After reload, it makes no difference, since pseudo regs have
been eliminated by then. */
 
/* Non-strict versions. */
#define REG_OK_FOR_INDEX_NONSTRICT_P(X) 0
/* Don't use REGNO_OK_FOR_BASE_P here because it uses reg_renumber. */
#define REG_OK_FOR_BASE_NONSTRICT_P(X) \
(REGNO (X) >= FIRST_PSEUDO_REGISTER || REGNO (X) != MAC_REG)
 
/* Strict versions. */
#define REG_OK_FOR_INDEX_STRICT_P(X) REGNO_OK_FOR_INDEX_P (REGNO (X))
#define REG_OK_FOR_BASE_STRICT_P(X) REGNO_OK_FOR_BASE_P (REGNO (X))
 
#ifndef REG_OK_STRICT
 
#define REG_OK_FOR_INDEX_P(X) REG_OK_FOR_INDEX_NONSTRICT_P (X)
#define REG_OK_FOR_BASE_P(X) REG_OK_FOR_BASE_NONSTRICT_P (X)
 
#else
 
#define REG_OK_FOR_INDEX_P(X) REG_OK_FOR_INDEX_STRICT_P (X)
#define REG_OK_FOR_BASE_P(X) REG_OK_FOR_BASE_STRICT_P (X)
 
#endif
 
/* Extra constraints. */
 
#define OK_FOR_Q(OP) \
(TARGET_H8300SX && memory_operand ((OP), VOIDmode))
 
#define OK_FOR_R(OP) \
(GET_CODE (OP) == CONST_INT \
? !h8300_shift_needs_scratch_p (INTVAL (OP), QImode) \
: 0)
 
#define OK_FOR_S(OP) \
(GET_CODE (OP) == CONST_INT \
? !h8300_shift_needs_scratch_p (INTVAL (OP), HImode) \
: 0)
 
#define OK_FOR_T(OP) \
(GET_CODE (OP) == CONST_INT \
? !h8300_shift_needs_scratch_p (INTVAL (OP), SImode) \
: 0)
 
/* 'U' if valid for a bset destination;
i.e. a register, register indirect, or the eightbit memory region
(a SYMBOL_REF with an SYMBOL_REF_FLAG set).
 
On the H8S 'U' can also be a 16bit or 32bit absolute. */
#define OK_FOR_U(OP) \
((GET_CODE (OP) == REG && REG_OK_FOR_BASE_P (OP)) \
|| (GET_CODE (OP) == MEM && GET_CODE (XEXP (OP, 0)) == REG \
&& REG_OK_FOR_BASE_P (XEXP (OP, 0))) \
|| (GET_CODE (OP) == MEM && GET_CODE (XEXP (OP, 0)) == SYMBOL_REF \
&& TARGET_H8300S) \
|| (GET_CODE (OP) == MEM && GET_CODE (XEXP (OP, 0)) == CONST \
&& GET_CODE (XEXP (XEXP (OP, 0), 0)) == PLUS \
&& GET_CODE (XEXP (XEXP (XEXP (OP, 0), 0), 0)) == SYMBOL_REF \
&& GET_CODE (XEXP (XEXP (XEXP (OP, 0), 0), 1)) == CONST_INT \
&& (TARGET_H8300S \
|| SYMBOL_REF_FLAG (XEXP (XEXP (XEXP (OP, 0), 0), 0)))) \
|| (GET_CODE (OP) == MEM \
&& h8300_eightbit_constant_address_p (XEXP (OP, 0))) \
|| (GET_CODE (OP) == MEM && TARGET_H8300S \
&& GET_CODE (XEXP (OP, 0)) == CONST_INT))
 
/* Multi-letter constraints starting with W are to be used for
operands that require a memory operand, i.e,. that are never used
along with register constraints (see EXTRA_MEMORY_CONSTRAINTS).
For operands that require a memory operand (or not) but that always
accept a register, a multi-letter constraint starting with Y should
be used instead. */
 
#define OK_FOR_WU(OP) \
(GET_CODE (OP) == MEM && OK_FOR_U (OP))
 
#define OK_FOR_W(OP, STR) \
((STR)[1] == 'U' ? OK_FOR_WU (OP) \
: 0)
 
#define CONSTRAINT_LEN_FOR_W(STR) \
((STR)[1] == 'U' ? 2 \
: 0)
 
/* We don't have any constraint starting with Y yet, but before
someone uses it for a one-letter constraint and we're left without
any upper-case constraints left, we reserve it for extensions
here. */
#define OK_FOR_Y(OP, STR) \
(0)
 
#define CONSTRAINT_LEN_FOR_Y(STR) \
(0)
 
#define OK_FOR_Z(OP) \
(TARGET_H8300SX \
&& GET_CODE (OP) == MEM \
&& CONSTANT_P (XEXP ((OP), 0)))
 
#define EXTRA_CONSTRAINT_STR(OP, C, STR) \
((C) == 'Q' ? OK_FOR_Q (OP) : \
(C) == 'R' ? OK_FOR_R (OP) : \
(C) == 'S' ? OK_FOR_S (OP) : \
(C) == 'T' ? OK_FOR_T (OP) : \
(C) == 'U' ? OK_FOR_U (OP) : \
(C) == 'W' ? OK_FOR_W ((OP), (STR)) : \
(C) == 'Y' ? OK_FOR_Y ((OP), (STR)) : \
(C) == 'Z' ? OK_FOR_Z (OP) : \
0)
 
#define CONSTRAINT_LEN(C, STR) \
((C) == 'P' ? CONSTRAINT_LEN_FOR_P (STR) \
: (C) == 'W' ? CONSTRAINT_LEN_FOR_W (STR) \
: (C) == 'Y' ? CONSTRAINT_LEN_FOR_Y (STR) \
: DEFAULT_CONSTRAINT_LEN ((C), (STR)))
 
/* Experiments suggest that it's better not add 'Q' or 'U' here. No
patterns need it for correctness (no patterns use 'Q' and 'U'
without also providing a register alternative). And defining it
will mean that a spilled pseudo could be replaced by its frame
location in several consecutive insns.
 
Instead, it seems to be better to force pseudos to be reloaded
into registers and then use peepholes to recombine insns when
beneficial.
 
Unfortunately, for WU (unlike plain U, that matches regs as well),
we must require a memory address. In fact, all multi-letter
constraints started with W are supposed to have this property, so
we just test for W here. */
#define EXTRA_MEMORY_CONSTRAINT(C, STR) \
((C) == 'W')
 
#ifndef REG_OK_STRICT
#define GO_IF_LEGITIMATE_ADDRESS(MODE, X, ADDR) \
do \
{ \
if (h8300_legitimate_address_p ((MODE), (X), 0)) \
goto ADDR; \
} \
while (0)
#else
#define GO_IF_LEGITIMATE_ADDRESS(MODE, X, ADDR) \
do \
{ \
if (h8300_legitimate_address_p ((MODE), (X), 1)) \
goto ADDR; \
} \
while (0)
#endif
/* Go to LABEL if ADDR (a legitimate address expression)
has an effect that depends on the machine mode it is used for.
 
On the H8/300, the predecrement and postincrement address depend thus
(the amount of decrement or increment being the length of the operand). */
 
#define GO_IF_MODE_DEPENDENT_ADDRESS(ADDR, LABEL) \
if (GET_CODE (ADDR) == POST_INC \
|| GET_CODE (ADDR) == POST_DEC \
|| GET_CODE (ADDR) == PRE_INC \
|| GET_CODE (ADDR) == PRE_DEC) \
goto LABEL; \
if (GET_CODE (ADDR) == PLUS \
&& h8300_get_index (XEXP (ADDR, 0), VOIDmode, 0) != XEXP (ADDR, 0)) \
goto LABEL;
/* Specify the machine mode that this machine uses
for the index in the tablejump instruction. */
#define CASE_VECTOR_MODE Pmode
 
/* Define this as 1 if `char' should by default be signed; else as 0.
 
On the H8/300, sign extension is expensive, so we'll say that chars
are unsigned. */
#define DEFAULT_SIGNED_CHAR 0
 
/* This flag, if defined, says the same insns that convert to a signed fixnum
also convert validly to an unsigned one. */
#define FIXUNS_TRUNC_LIKE_FIX_TRUNC
 
/* Max number of bytes we can move from memory to memory
in one reasonably fast instruction. */
#define MOVE_MAX (TARGET_H8300H || TARGET_H8300S ? 4 : 2)
#define MAX_MOVE_MAX 4
 
/* Nonzero if access to memory by bytes is slow and undesirable. */
#define SLOW_BYTE_ACCESS TARGET_SLOWBYTE
 
/* Define if shifts truncate the shift count
which implies one can omit a sign-extension or zero-extension
of a shift count. */
/* #define SHIFT_COUNT_TRUNCATED */
 
/* Value is 1 if truncating an integer of INPREC bits to OUTPREC bits
is done just by pretending it is already truncated. */
#define TRULY_NOOP_TRUNCATION(OUTPREC, INPREC) 1
 
/* Specify the machine mode that pointers have.
After generation of rtl, the compiler makes no further distinction
between pointers and any other objects of this machine mode. */
#define Pmode \
((TARGET_H8300H || TARGET_H8300S) && !TARGET_NORMAL_MODE ? SImode : HImode)
 
/* ANSI C types.
We use longs for the H8/300H and the H8S because ints can be 16 or 32.
GCC requires SIZE_TYPE to be the same size as pointers. */
#define SIZE_TYPE \
(TARGET_H8300 || TARGET_NORMAL_MODE ? TARGET_INT32 ? "short unsigned int" : "unsigned int" : "long unsigned int")
#define PTRDIFF_TYPE \
(TARGET_H8300 || TARGET_NORMAL_MODE ? TARGET_INT32 ? "short int" : "int" : "long int")
 
#define POINTER_SIZE \
((TARGET_H8300H || TARGET_H8300S) && !TARGET_NORMAL_MODE ? 32 : 16)
 
#define WCHAR_TYPE "short unsigned int"
#define WCHAR_TYPE_SIZE 16
 
/* A function address in a call instruction
is a byte address (for indexing purposes)
so give the MEM rtx a byte's mode. */
#define FUNCTION_MODE QImode
 
/* Return the length of JUMP's delay slot insn (0 if it has none).
If JUMP is a delayed branch, NEXT_INSN (PREV_INSN (JUMP)) will
be the containing SEQUENCE, not JUMP itself. */
#define DELAY_SLOT_LENGTH(JUMP) \
(NEXT_INSN (PREV_INSN (JUMP)) == JUMP ? 0 : 2)
 
#define BRANCH_COST 0
 
/* Tell final.c how to eliminate redundant test instructions. */
 
/* Here we define machine-dependent flags and fields in cc_status
(see `conditions.h'). No extra ones are needed for the h8300. */
 
/* Store in cc_status the expressions
that the condition codes will describe
after execution of an instruction whose pattern is EXP.
Do not alter them if the instruction would not alter the cc's. */
 
#define NOTICE_UPDATE_CC(EXP, INSN) notice_update_cc (EXP, INSN)
 
/* The add insns don't set overflow in a usable way. */
#define CC_OVERFLOW_UNUSABLE 01000
/* The mov,and,or,xor insns don't set carry. That's OK though as the
Z bit is all we need when doing unsigned comparisons on the result of
these insns (since they're always with 0). However, conditions.h has
CC_NO_OVERFLOW defined for this purpose. Rename it to something more
understandable. */
#define CC_NO_CARRY CC_NO_OVERFLOW
/* Control the assembler format that we output. */
 
/* Output to assembler file text saying following lines
may contain character constants, extra white space, comments, etc. */
 
#define ASM_APP_ON "; #APP\n"
 
/* Output to assembler file text saying following lines
no longer contain unusual constructs. */
 
#define ASM_APP_OFF "; #NO_APP\n"
 
#define FILE_ASM_OP "\t.file\n"
 
/* The assembler op to get a word, 2 bytes for the H8/300, 4 for H8/300H. */
#define ASM_WORD_OP \
(TARGET_H8300 || TARGET_NORMAL_MODE ? "\t.word\t" : "\t.long\t")
 
#define TEXT_SECTION_ASM_OP "\t.section .text"
#define DATA_SECTION_ASM_OP "\t.section .data"
#define BSS_SECTION_ASM_OP "\t.section .bss"
 
#undef DO_GLOBAL_CTORS_BODY
#define DO_GLOBAL_CTORS_BODY \
{ \
extern func_ptr __ctors[]; \
extern func_ptr __ctors_end[]; \
func_ptr *p; \
for (p = __ctors_end; p > __ctors; ) \
{ \
(*--p)(); \
} \
}
 
#undef DO_GLOBAL_DTORS_BODY
#define DO_GLOBAL_DTORS_BODY \
{ \
extern func_ptr __dtors[]; \
extern func_ptr __dtors_end[]; \
func_ptr *p; \
for (p = __dtors; p < __dtors_end; p++) \
{ \
(*p)(); \
} \
}
 
/* How to refer to registers in assembler output.
This sequence is indexed by compiler's hard-register-number (see above). */
 
#define REGISTER_NAMES \
{ "r0", "r1", "r2", "r3", "r4", "r5", "r6", "sp", "mac", "ap", "rap", "fp" }
 
#define ADDITIONAL_REGISTER_NAMES \
{ {"er0", 0}, {"er1", 1}, {"er2", 2}, {"er3", 3}, {"er4", 4}, \
{"er5", 5}, {"er6", 6}, {"er7", 7}, {"r7", 7} }
 
/* Globalizing directive for a label. */
#define GLOBAL_ASM_OP "\t.global "
 
#define ASM_DECLARE_FUNCTION_NAME(FILE, NAME, DECL) \
ASM_OUTPUT_LABEL (FILE, NAME)
 
/* The prefix to add to user-visible assembler symbols. */
 
#define USER_LABEL_PREFIX "_"
 
/* This is how to store into the string LABEL
the symbol_ref name of an internal numbered label where
PREFIX is the class of label and NUM is the number within the class.
This is suitable for output with `assemble_name'.
 
N.B.: The h8300.md branch_true and branch_false patterns also know
how to generate internal labels. */
#define ASM_GENERATE_INTERNAL_LABEL(LABEL, PREFIX, NUM) \
sprintf (LABEL, "*.%s%lu", PREFIX, (unsigned long)(NUM))
 
/* This is how to output an insn to push a register on the stack.
It need not be very fast code. */
 
#define ASM_OUTPUT_REG_PUSH(FILE, REGNO) \
fprintf (FILE, "\t%s\t%s\n", h8_push_op, h8_reg_names[REGNO])
 
/* This is how to output an insn to pop a register from the stack.
It need not be very fast code. */
 
#define ASM_OUTPUT_REG_POP(FILE, REGNO) \
fprintf (FILE, "\t%s\t%s\n", h8_pop_op, h8_reg_names[REGNO])
 
/* This is how to output an element of a case-vector that is absolute. */
 
#define ASM_OUTPUT_ADDR_VEC_ELT(FILE, VALUE) \
fprintf (FILE, "%s.L%d\n", ASM_WORD_OP, VALUE)
 
/* This is how to output an element of a case-vector that is relative. */
 
#define ASM_OUTPUT_ADDR_DIFF_ELT(FILE, BODY, VALUE, REL) \
fprintf (FILE, "%s.L%d-.L%d\n", ASM_WORD_OP, VALUE, REL)
 
/* This is how to output an assembler line
that says to advance the location counter
to a multiple of 2**LOG bytes. */
 
#define ASM_OUTPUT_ALIGN(FILE, LOG) \
if ((LOG) != 0) \
fprintf (FILE, "\t.align %d\n", (LOG))
 
#define ASM_OUTPUT_SKIP(FILE, SIZE) \
fprintf (FILE, "\t.space %d\n", (int)(SIZE))
 
/* This says how to output an assembler line
to define a global common symbol. */
 
#define ASM_OUTPUT_COMMON(FILE, NAME, SIZE, ROUNDED) \
( fputs ("\t.comm ", (FILE)), \
assemble_name ((FILE), (NAME)), \
fprintf ((FILE), ",%lu\n", (unsigned long)(SIZE)))
 
/* This says how to output the assembler to define a global
uninitialized but not common symbol.
Try to use asm_output_bss to implement this macro. */
 
#define ASM_OUTPUT_BSS(FILE, DECL, NAME, SIZE, ROUNDED) \
asm_output_bss ((FILE), (DECL), (NAME), (SIZE), (ROUNDED))
 
#define ASM_OUTPUT_ALIGNED_BSS(FILE, DECL, NAME, SIZE, ALIGN) \
asm_output_aligned_bss (FILE, DECL, NAME, SIZE, ALIGN)
 
/* This says how to output an assembler line
to define a local common symbol. */
 
#define ASM_OUTPUT_LOCAL(FILE, NAME, SIZE, ROUNDED) \
( fputs ("\t.lcomm ", (FILE)), \
assemble_name ((FILE), (NAME)), \
fprintf ((FILE), ",%d\n", (int)(SIZE)))
 
#define ASM_PN_FORMAT "%s___%lu"
 
/* Print an instruction operand X on file FILE.
Look in h8300.c for details. */
 
#define PRINT_OPERAND_PUNCT_VALID_P(CODE) \
((CODE) == '#')
 
#define PRINT_OPERAND(FILE, X, CODE) print_operand (FILE, X, CODE)
 
/* Print a memory operand whose address is X, on file FILE.
This uses a function in h8300.c. */
 
#define PRINT_OPERAND_ADDRESS(FILE, ADDR) print_operand_address (FILE, ADDR)
 
/* H8300 specific pragmas. */
#define REGISTER_TARGET_PRAGMAS() \
do \
{ \
c_register_pragma (0, "saveall", h8300_pr_saveall); \
c_register_pragma (0, "interrupt", h8300_pr_interrupt); \
} \
while (0)
 
#define FINAL_PRESCAN_INSN(insn, operand, nop) \
final_prescan_insn (insn, operand, nop)
 
#define MOVE_RATIO 3
extern int h8300_move_ratio;
#undef MOVE_RATIO
#define MOVE_RATIO h8300_move_ratio
 
/* 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)
 
#endif /* ! GCC_H8300_H */
/t-rtems
0,0 → 1,7
# Custom multilibs for RTEMS
 
# -mn is not applicable to RTEMS (-mn implies 16bit void*)
 
MULTILIB_OPTIONS = mh/ms mint32
MULTILIB_DIRNAMES = h8300h h8300s int32
MULTILIB_EXCEPTIONS = mint32
/popcounthi2.c
0,0 → 1,41
/* The implementation of __popcounthi2.
Copyright (C) 2003 Free Software Foundation, Inc.
 
This file is part of GCC.
 
GCC is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
 
In addition to the permissions in the GNU General Public License, the
Free Software Foundation gives you unlimited permission to link the
compiled version of this file into combinations with other programs,
and to distribute those combinations without any restriction coming
from the use of this file. (The General Public License restrictions
do apply in other respects; for example, they cover modification of
the file, and distribution when not linked into a combine
executable.)
 
GCC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
 
You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING. If not, write to
the Free Software Foundation, 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA. */
 
int __popcounthi2 (unsigned short x);
 
int
__popcounthi2 (unsigned short x)
{
int i;
int count = 0;
for (i = 0; i < 16; i++)
if (x & ((unsigned short) 1 << i))
count++;
return count;
}
/parityhi2.c
0,0 → 1,41
/* The implementation of __parityhi2.
Copyright (C) 2003 Free Software Foundation, Inc.
 
This file is part of GCC.
 
GCC is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
 
In addition to the permissions in the GNU General Public License, the
Free Software Foundation gives you unlimited permission to link the
compiled version of this file into combinations with other programs,
and to distribute those combinations without any restriction coming
from the use of this file. (The General Public License restrictions
do apply in other respects; for example, they cover modification of
the file, and distribution when not linked into a combine
executable.)
 
GCC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
 
You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING. If not, write to
the Free Software Foundation, 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA. */
 
int __parityhi2 (unsigned short x);
 
int
__parityhi2 (unsigned short x)
{
int i;
int count = 0;
for (i = 0; i < 16; i++)
if (x & ((unsigned short) 1 << i))
count++;
return count & 1;
}
/elf.h
0,0 → 1,44
/* Definitions of target machine for GNU compiler.
Renesas H8/300 version generating elf
Copyright (C) 2001, 2002, 2003, 2004, 2007 Free Software Foundation, Inc.
Contributed by Steve Chamberlain (sac@cygnus.com),
Jim Wilson (wilson@cygnus.com), and Doug Evans (dje@cygnus.com).
 
This file is part of GCC.
 
GCC is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3, or (at your option)
any later version.
 
GCC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
 
You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING3. If not see
<http://www.gnu.org/licenses/>. */
 
#ifndef GCC_H8300_ELF_H
#define GCC_H8300_ELF_H
 
#undef ENDFILE_SPEC
#define ENDFILE_SPEC "crtend.o%s %{pg:gcrtn.o%s}%{!pg:crtn.o%s}"
 
#undef STARTFILE_SPEC
#define STARTFILE_SPEC "%{!shared: \
%{!symbolic: \
%{pg:gcrt0.o%s}%{!pg:%{p:mcrt0.o%s}%{!p:crt0.o%s}}}}\
%{pg:gcrti.o%s}%{!pg:crti.o%s} \
crtbegin.o%s"
 
#undef USER_LABEL_PREFIX
#define USER_LABEL_PREFIX "_"
 
#define JUMP_TABLES_IN_TEXT_SECTION (flag_pic)
 
#undef LINK_SPEC
#define LINK_SPEC "%{mh:%{mn:-m h8300hnelf}} %{mh:%{!mn:-m h8300helf}} %{ms:%{mn:-m h8300snelf}} %{ms:%{!mn:-m h8300self}} %{msx:%{mn:-m h8300sxnelf;:-m h8300sxelf}}"
 
#endif /* h8300/elf.h */
/mova.md
0,0 → 1,841
;; -*- buffer-read-only: t -*-
;; Generated automatically from genmova.sh
(define_insn ""
[(set (match_operand:QI 0 "register_operand" "=r,r")
(plus:QI (mult:QI (match_operand:QI 1 "h8300_dst_operand" "0,rQ")
(const_int 2))
(match_operand:QI 2 "immediate_operand" "i,i")))]
"TARGET_H8300SX"
"mova/w.l @(%o2,%X1.b),%S0"
[(set_attr "length_table" "mova")
(set_attr "cc" "none")])
 
(define_insn ""
[(set (match_operand:QI 0 "register_operand" "=r,r")
(plus:QI (ashift:QI (match_operand:QI 1 "h8300_dst_operand" "0,rQ")
(const_int 1))
(match_operand:QI 2 "immediate_operand" "i,i")))]
"TARGET_H8300SX"
"mova/w.l @(%o2,%X1.b),%S0"
[(set_attr "length_table" "mova")
(set_attr "cc" "none")])
 
(define_insn ""
[(set (match_operand:QI 0 "register_operand" "=r,r")
(plus:QI (mult:QI (match_operand:QI 1 "h8300_dst_operand" "0,rQ")
(const_int 4))
(match_operand:QI 2 "immediate_operand" "i,i")))]
"TARGET_H8300SX"
"mova/l.l @(%o2,%X1.b),%S0"
[(set_attr "length_table" "mova")
(set_attr "cc" "none")])
 
(define_insn ""
[(set (match_operand:QI 0 "register_operand" "=r,r")
(plus:QI (ashift:QI (match_operand:QI 1 "h8300_dst_operand" "0,rQ")
(const_int 2))
(match_operand:QI 2 "immediate_operand" "i,i")))]
"TARGET_H8300SX"
"mova/l.l @(%o2,%X1.b),%S0"
[(set_attr "length_table" "mova")
(set_attr "cc" "none")])
 
(define_insn ""
[(set (match_operand:HI 0 "register_operand" "=r,r")
(plus:HI (zero_extend:HI (match_operand:QI 1 "h8300_dst_operand" "0,rQ"))
(match_operand:HI 2 "immediate_operand" "i,i")))]
"TARGET_H8300SX"
"mova/b.l @(%o2,%X1.b),%S0"
[(set_attr "length_table" "mova")
(set_attr "cc" "none")])
 
(define_insn ""
[(set (match_operand:HI 0 "register_operand" "=r,r")
(mult:HI (zero_extend:HI (match_operand:QI 1 "h8300_dst_operand" "0,rQ"))
(const_int 2)))]
"TARGET_H8300SX"
"mova/w.l @(0,%X1.b),%S0"
[(set_attr "length_table" "mova_zero")
(set_attr "cc" "none")])
 
(define_insn ""
[(set (match_operand:HI 0 "register_operand" "=r,r")
(plus:HI (mult:HI (zero_extend:HI (match_operand:QI 1 "h8300_dst_operand" "0,rQ"))
(const_int 2))
(match_operand:HI 2 "immediate_operand" "i,i")))]
"TARGET_H8300SX"
"mova/w.l @(%o2,%X1.b),%S0"
[(set_attr "length_table" "mova")
(set_attr "cc" "none")])
 
(define_insn ""
[(set (match_operand:HI 0 "register_operand" "=r")
(and:HI (mult:HI (subreg:HI (match_operand:QI 1 "memory_operand" "m") 0)
(const_int 2))
(const_int 510)))]
"TARGET_H8300SX"
"mova/w.l @(0,%X1.b),%S0"
[(set_attr "length_table" "mova_zero")
(set_attr "cc" "none")])
 
(define_insn ""
[(set (match_operand:HI 0 "register_operand" "=r")
(plus:HI (and:HI (mult:HI (subreg:HI (match_operand:QI 1 "memory_operand" "m") 0)
(const_int 2))
(const_int 510))
(match_operand:HI 2 "immediate_operand" "i")))]
"TARGET_H8300SX"
"mova/w.l @(%o2,%X1.b),%S0"
[(set_attr "length_table" "mova")
(set_attr "cc" "none")])
 
(define_insn ""
[(set (match_operand:HI 0 "register_operand" "=r")
(and:HI (mult:HI (match_operand:HI 1 "register_operand" "0")
(const_int 2))
(const_int 510)))]
"TARGET_H8300SX"
"mova/w.l @(0,%X1.b),%S0"
[(set_attr "length_table" "mova_zero")
(set_attr "cc" "none")])
 
(define_insn ""
[(set (match_operand:HI 0 "register_operand" "=r")
(plus:HI (and:HI (mult:HI (match_operand:HI 1 "register_operand" "0")
(const_int 2))
(const_int 510))
(match_operand:HI 2 "immediate_operand" "i")))]
"TARGET_H8300SX"
"mova/w.l @(%o2,%X1.b),%S0"
[(set_attr "length_table" "mova")
(set_attr "cc" "none")])
 
(define_insn ""
[(set (match_operand:HI 0 "register_operand" "=r,r")
(ashift:HI (zero_extend:HI (match_operand:QI 1 "h8300_dst_operand" "0,rQ"))
(const_int 1)))]
"TARGET_H8300SX"
"mova/w.l @(0,%X1.b),%S0"
[(set_attr "length_table" "mova_zero")
(set_attr "cc" "none")])
 
(define_insn ""
[(set (match_operand:HI 0 "register_operand" "=r,r")
(plus:HI (ashift:HI (zero_extend:HI (match_operand:QI 1 "h8300_dst_operand" "0,rQ"))
(const_int 1))
(match_operand:HI 2 "immediate_operand" "i,i")))]
"TARGET_H8300SX"
"mova/w.l @(%o2,%X1.b),%S0"
[(set_attr "length_table" "mova")
(set_attr "cc" "none")])
 
(define_insn ""
[(set (match_operand:HI 0 "register_operand" "=r")
(and:HI (ashift:HI (subreg:HI (match_operand:QI 1 "memory_operand" "m") 0)
(const_int 1))
(const_int 510)))]
"TARGET_H8300SX"
"mova/w.l @(0,%X1.b),%S0"
[(set_attr "length_table" "mova_zero")
(set_attr "cc" "none")])
 
(define_insn ""
[(set (match_operand:HI 0 "register_operand" "=r")
(plus:HI (and:HI (ashift:HI (subreg:HI (match_operand:QI 1 "memory_operand" "m") 0)
(const_int 1))
(const_int 510))
(match_operand:HI 2 "immediate_operand" "i")))]
"TARGET_H8300SX"
"mova/w.l @(%o2,%X1.b),%S0"
[(set_attr "length_table" "mova")
(set_attr "cc" "none")])
 
(define_insn ""
[(set (match_operand:HI 0 "register_operand" "=r")
(and:HI (ashift:HI (match_operand:HI 1 "register_operand" "0")
(const_int 1))
(const_int 510)))]
"TARGET_H8300SX"
"mova/w.l @(0,%X1.b),%S0"
[(set_attr "length_table" "mova_zero")
(set_attr "cc" "none")])
 
(define_insn ""
[(set (match_operand:HI 0 "register_operand" "=r")
(plus:HI (and:HI (ashift:HI (match_operand:HI 1 "register_operand" "0")
(const_int 1))
(const_int 510))
(match_operand:HI 2 "immediate_operand" "i")))]
"TARGET_H8300SX"
"mova/w.l @(%o2,%X1.b),%S0"
[(set_attr "length_table" "mova")
(set_attr "cc" "none")])
 
(define_insn ""
[(set (match_operand:HI 0 "register_operand" "=r,r")
(mult:HI (zero_extend:HI (match_operand:QI 1 "h8300_dst_operand" "0,rQ"))
(const_int 4)))]
"TARGET_H8300SX"
"mova/l.l @(0,%X1.b),%S0"
[(set_attr "length_table" "mova_zero")
(set_attr "cc" "none")])
 
(define_insn ""
[(set (match_operand:HI 0 "register_operand" "=r,r")
(plus:HI (mult:HI (zero_extend:HI (match_operand:QI 1 "h8300_dst_operand" "0,rQ"))
(const_int 4))
(match_operand:HI 2 "immediate_operand" "i,i")))]
"TARGET_H8300SX"
"mova/l.l @(%o2,%X1.b),%S0"
[(set_attr "length_table" "mova")
(set_attr "cc" "none")])
 
(define_insn ""
[(set (match_operand:HI 0 "register_operand" "=r")
(and:HI (mult:HI (subreg:HI (match_operand:QI 1 "memory_operand" "m") 0)
(const_int 4))
(const_int 1020)))]
"TARGET_H8300SX"
"mova/l.l @(0,%X1.b),%S0"
[(set_attr "length_table" "mova_zero")
(set_attr "cc" "none")])
 
(define_insn ""
[(set (match_operand:HI 0 "register_operand" "=r")
(plus:HI (and:HI (mult:HI (subreg:HI (match_operand:QI 1 "memory_operand" "m") 0)
(const_int 4))
(const_int 1020))
(match_operand:HI 2 "immediate_operand" "i")))]
"TARGET_H8300SX"
"mova/l.l @(%o2,%X1.b),%S0"
[(set_attr "length_table" "mova")
(set_attr "cc" "none")])
 
(define_insn ""
[(set (match_operand:HI 0 "register_operand" "=r")
(and:HI (mult:HI (match_operand:HI 1 "register_operand" "0")
(const_int 4))
(const_int 1020)))]
"TARGET_H8300SX"
"mova/l.l @(0,%X1.b),%S0"
[(set_attr "length_table" "mova_zero")
(set_attr "cc" "none")])
 
(define_insn ""
[(set (match_operand:HI 0 "register_operand" "=r")
(plus:HI (and:HI (mult:HI (match_operand:HI 1 "register_operand" "0")
(const_int 4))
(const_int 1020))
(match_operand:HI 2 "immediate_operand" "i")))]
"TARGET_H8300SX"
"mova/l.l @(%o2,%X1.b),%S0"
[(set_attr "length_table" "mova")
(set_attr "cc" "none")])
 
(define_insn ""
[(set (match_operand:HI 0 "register_operand" "=r,r")
(ashift:HI (zero_extend:HI (match_operand:QI 1 "h8300_dst_operand" "0,rQ"))
(const_int 2)))]
"TARGET_H8300SX"
"mova/l.l @(0,%X1.b),%S0"
[(set_attr "length_table" "mova_zero")
(set_attr "cc" "none")])
 
(define_insn ""
[(set (match_operand:HI 0 "register_operand" "=r,r")
(plus:HI (ashift:HI (zero_extend:HI (match_operand:QI 1 "h8300_dst_operand" "0,rQ"))
(const_int 2))
(match_operand:HI 2 "immediate_operand" "i,i")))]
"TARGET_H8300SX"
"mova/l.l @(%o2,%X1.b),%S0"
[(set_attr "length_table" "mova")
(set_attr "cc" "none")])
 
(define_insn ""
[(set (match_operand:HI 0 "register_operand" "=r")
(and:HI (ashift:HI (subreg:HI (match_operand:QI 1 "memory_operand" "m") 0)
(const_int 2))
(const_int 1020)))]
"TARGET_H8300SX"
"mova/l.l @(0,%X1.b),%S0"
[(set_attr "length_table" "mova_zero")
(set_attr "cc" "none")])
 
(define_insn ""
[(set (match_operand:HI 0 "register_operand" "=r")
(plus:HI (and:HI (ashift:HI (subreg:HI (match_operand:QI 1 "memory_operand" "m") 0)
(const_int 2))
(const_int 1020))
(match_operand:HI 2 "immediate_operand" "i")))]
"TARGET_H8300SX"
"mova/l.l @(%o2,%X1.b),%S0"
[(set_attr "length_table" "mova")
(set_attr "cc" "none")])
 
(define_insn ""
[(set (match_operand:HI 0 "register_operand" "=r")
(and:HI (ashift:HI (match_operand:HI 1 "register_operand" "0")
(const_int 2))
(const_int 1020)))]
"TARGET_H8300SX"
"mova/l.l @(0,%X1.b),%S0"
[(set_attr "length_table" "mova_zero")
(set_attr "cc" "none")])
 
(define_insn ""
[(set (match_operand:HI 0 "register_operand" "=r")
(plus:HI (and:HI (ashift:HI (match_operand:HI 1 "register_operand" "0")
(const_int 2))
(const_int 1020))
(match_operand:HI 2 "immediate_operand" "i")))]
"TARGET_H8300SX"
"mova/l.l @(%o2,%X1.b),%S0"
[(set_attr "length_table" "mova")
(set_attr "cc" "none")])
 
(define_insn ""
[(set (match_operand:SI 0 "register_operand" "=r,r")
(plus:SI (zero_extend:SI (match_operand:QI 1 "h8300_dst_operand" "0,rQ"))
(match_operand:SI 2 "immediate_operand" "i,i")))]
"TARGET_H8300SX"
"mova/b.l @(%o2,%X1.b),%S0"
[(set_attr "length_table" "mova")
(set_attr "cc" "none")])
 
(define_insn ""
[(set (match_operand:SI 0 "register_operand" "=r,r")
(mult:SI (zero_extend:SI (match_operand:QI 1 "h8300_dst_operand" "0,rQ"))
(const_int 2)))]
"TARGET_H8300SX"
"mova/w.l @(0,%X1.b),%S0"
[(set_attr "length_table" "mova_zero")
(set_attr "cc" "none")])
 
(define_insn ""
[(set (match_operand:SI 0 "register_operand" "=r,r")
(plus:SI (mult:SI (zero_extend:SI (match_operand:QI 1 "h8300_dst_operand" "0,rQ"))
(const_int 2))
(match_operand:SI 2 "immediate_operand" "i,i")))]
"TARGET_H8300SX"
"mova/w.l @(%o2,%X1.b),%S0"
[(set_attr "length_table" "mova")
(set_attr "cc" "none")])
 
(define_insn ""
[(set (match_operand:SI 0 "register_operand" "=r")
(and:SI (mult:SI (subreg:SI (match_operand:QI 1 "memory_operand" "m") 0)
(const_int 2))
(const_int 510)))]
"TARGET_H8300SX"
"mova/w.l @(0,%X1.b),%S0"
[(set_attr "length_table" "mova_zero")
(set_attr "cc" "none")])
 
(define_insn ""
[(set (match_operand:SI 0 "register_operand" "=r")
(plus:SI (and:SI (mult:SI (subreg:SI (match_operand:QI 1 "memory_operand" "m") 0)
(const_int 2))
(const_int 510))
(match_operand:SI 2 "immediate_operand" "i")))]
"TARGET_H8300SX"
"mova/w.l @(%o2,%X1.b),%S0"
[(set_attr "length_table" "mova")
(set_attr "cc" "none")])
 
(define_insn ""
[(set (match_operand:SI 0 "register_operand" "=r")
(and:SI (mult:SI (match_operand:SI 1 "register_operand" "0")
(const_int 2))
(const_int 510)))]
"TARGET_H8300SX"
"mova/w.l @(0,%X1.b),%S0"
[(set_attr "length_table" "mova_zero")
(set_attr "cc" "none")])
 
(define_insn ""
[(set (match_operand:SI 0 "register_operand" "=r")
(plus:SI (and:SI (mult:SI (match_operand:SI 1 "register_operand" "0")
(const_int 2))
(const_int 510))
(match_operand:SI 2 "immediate_operand" "i")))]
"TARGET_H8300SX"
"mova/w.l @(%o2,%X1.b),%S0"
[(set_attr "length_table" "mova")
(set_attr "cc" "none")])
 
(define_insn ""
[(set (match_operand:SI 0 "register_operand" "=r,r")
(ashift:SI (zero_extend:SI (match_operand:QI 1 "h8300_dst_operand" "0,rQ"))
(const_int 1)))]
"TARGET_H8300SX"
"mova/w.l @(0,%X1.b),%S0"
[(set_attr "length_table" "mova_zero")
(set_attr "cc" "none")])
 
(define_insn ""
[(set (match_operand:SI 0 "register_operand" "=r,r")
(plus:SI (ashift:SI (zero_extend:SI (match_operand:QI 1 "h8300_dst_operand" "0,rQ"))
(const_int 1))
(match_operand:SI 2 "immediate_operand" "i,i")))]
"TARGET_H8300SX"
"mova/w.l @(%o2,%X1.b),%S0"
[(set_attr "length_table" "mova")
(set_attr "cc" "none")])
 
(define_insn ""
[(set (match_operand:SI 0 "register_operand" "=r")
(and:SI (ashift:SI (subreg:SI (match_operand:QI 1 "memory_operand" "m") 0)
(const_int 1))
(const_int 510)))]
"TARGET_H8300SX"
"mova/w.l @(0,%X1.b),%S0"
[(set_attr "length_table" "mova_zero")
(set_attr "cc" "none")])
 
(define_insn ""
[(set (match_operand:SI 0 "register_operand" "=r")
(plus:SI (and:SI (ashift:SI (subreg:SI (match_operand:QI 1 "memory_operand" "m") 0)
(const_int 1))
(const_int 510))
(match_operand:SI 2 "immediate_operand" "i")))]
"TARGET_H8300SX"
"mova/w.l @(%o2,%X1.b),%S0"
[(set_attr "length_table" "mova")
(set_attr "cc" "none")])
 
(define_insn ""
[(set (match_operand:SI 0 "register_operand" "=r")
(and:SI (ashift:SI (match_operand:SI 1 "register_operand" "0")
(const_int 1))
(const_int 510)))]
"TARGET_H8300SX"
"mova/w.l @(0,%X1.b),%S0"
[(set_attr "length_table" "mova_zero")
(set_attr "cc" "none")])
 
(define_insn ""
[(set (match_operand:SI 0 "register_operand" "=r")
(plus:SI (and:SI (ashift:SI (match_operand:SI 1 "register_operand" "0")
(const_int 1))
(const_int 510))
(match_operand:SI 2 "immediate_operand" "i")))]
"TARGET_H8300SX"
"mova/w.l @(%o2,%X1.b),%S0"
[(set_attr "length_table" "mova")
(set_attr "cc" "none")])
 
(define_insn ""
[(set (match_operand:SI 0 "register_operand" "=r,r")
(mult:SI (zero_extend:SI (match_operand:QI 1 "h8300_dst_operand" "0,rQ"))
(const_int 4)))]
"TARGET_H8300SX"
"mova/l.l @(0,%X1.b),%S0"
[(set_attr "length_table" "mova_zero")
(set_attr "cc" "none")])
 
(define_insn ""
[(set (match_operand:SI 0 "register_operand" "=r,r")
(plus:SI (mult:SI (zero_extend:SI (match_operand:QI 1 "h8300_dst_operand" "0,rQ"))
(const_int 4))
(match_operand:SI 2 "immediate_operand" "i,i")))]
"TARGET_H8300SX"
"mova/l.l @(%o2,%X1.b),%S0"
[(set_attr "length_table" "mova")
(set_attr "cc" "none")])
 
(define_insn ""
[(set (match_operand:SI 0 "register_operand" "=r")
(and:SI (mult:SI (subreg:SI (match_operand:QI 1 "memory_operand" "m") 0)
(const_int 4))
(const_int 1020)))]
"TARGET_H8300SX"
"mova/l.l @(0,%X1.b),%S0"
[(set_attr "length_table" "mova_zero")
(set_attr "cc" "none")])
 
(define_insn ""
[(set (match_operand:SI 0 "register_operand" "=r")
(plus:SI (and:SI (mult:SI (subreg:SI (match_operand:QI 1 "memory_operand" "m") 0)
(const_int 4))
(const_int 1020))
(match_operand:SI 2 "immediate_operand" "i")))]
"TARGET_H8300SX"
"mova/l.l @(%o2,%X1.b),%S0"
[(set_attr "length_table" "mova")
(set_attr "cc" "none")])
 
(define_insn ""
[(set (match_operand:SI 0 "register_operand" "=r")
(and:SI (mult:SI (match_operand:SI 1 "register_operand" "0")
(const_int 4))
(const_int 1020)))]
"TARGET_H8300SX"
"mova/l.l @(0,%X1.b),%S0"
[(set_attr "length_table" "mova_zero")
(set_attr "cc" "none")])
 
(define_insn ""
[(set (match_operand:SI 0 "register_operand" "=r")
(plus:SI (and:SI (mult:SI (match_operand:SI 1 "register_operand" "0")
(const_int 4))
(const_int 1020))
(match_operand:SI 2 "immediate_operand" "i")))]
"TARGET_H8300SX"
"mova/l.l @(%o2,%X1.b),%S0"
[(set_attr "length_table" "mova")
(set_attr "cc" "none")])
 
(define_insn ""
[(set (match_operand:SI 0 "register_operand" "=r,r")
(ashift:SI (zero_extend:SI (match_operand:QI 1 "h8300_dst_operand" "0,rQ"))
(const_int 2)))]
"TARGET_H8300SX"
"mova/l.l @(0,%X1.b),%S0"
[(set_attr "length_table" "mova_zero")
(set_attr "cc" "none")])
 
(define_insn ""
[(set (match_operand:SI 0 "register_operand" "=r,r")
(plus:SI (ashift:SI (zero_extend:SI (match_operand:QI 1 "h8300_dst_operand" "0,rQ"))
(const_int 2))
(match_operand:SI 2 "immediate_operand" "i,i")))]
"TARGET_H8300SX"
"mova/l.l @(%o2,%X1.b),%S0"
[(set_attr "length_table" "mova")
(set_attr "cc" "none")])
 
(define_insn ""
[(set (match_operand:SI 0 "register_operand" "=r")
(and:SI (ashift:SI (subreg:SI (match_operand:QI 1 "memory_operand" "m") 0)
(const_int 2))
(const_int 1020)))]
"TARGET_H8300SX"
"mova/l.l @(0,%X1.b),%S0"
[(set_attr "length_table" "mova_zero")
(set_attr "cc" "none")])
 
(define_insn ""
[(set (match_operand:SI 0 "register_operand" "=r")
(plus:SI (and:SI (ashift:SI (subreg:SI (match_operand:QI 1 "memory_operand" "m") 0)
(const_int 2))
(const_int 1020))
(match_operand:SI 2 "immediate_operand" "i")))]
"TARGET_H8300SX"
"mova/l.l @(%o2,%X1.b),%S0"
[(set_attr "length_table" "mova")
(set_attr "cc" "none")])
 
(define_insn ""
[(set (match_operand:SI 0 "register_operand" "=r")
(and:SI (ashift:SI (match_operand:SI 1 "register_operand" "0")
(const_int 2))
(const_int 1020)))]
"TARGET_H8300SX"
"mova/l.l @(0,%X1.b),%S0"
[(set_attr "length_table" "mova_zero")
(set_attr "cc" "none")])
 
(define_insn ""
[(set (match_operand:SI 0 "register_operand" "=r")
(plus:SI (and:SI (ashift:SI (match_operand:SI 1 "register_operand" "0")
(const_int 2))
(const_int 1020))
(match_operand:SI 2 "immediate_operand" "i")))]
"TARGET_H8300SX"
"mova/l.l @(%o2,%X1.b),%S0"
[(set_attr "length_table" "mova")
(set_attr "cc" "none")])
 
(define_insn ""
[(set (match_operand:HI 0 "register_operand" "=r,r")
(plus:HI (mult:HI (match_operand:HI 1 "h8300_dst_operand" "0,rQ")
(const_int 2))
(match_operand:HI 2 "immediate_operand" "i,i")))]
"TARGET_H8300SX"
"mova/w.l @(%o2,%T1.w),%S0"
[(set_attr "length_table" "mova")
(set_attr "cc" "none")])
 
(define_insn ""
[(set (match_operand:HI 0 "register_operand" "=r,r")
(plus:HI (ashift:HI (match_operand:HI 1 "h8300_dst_operand" "0,rQ")
(const_int 1))
(match_operand:HI 2 "immediate_operand" "i,i")))]
"TARGET_H8300SX"
"mova/w.l @(%o2,%T1.w),%S0"
[(set_attr "length_table" "mova")
(set_attr "cc" "none")])
 
(define_insn ""
[(set (match_operand:HI 0 "register_operand" "=r,r")
(plus:HI (mult:HI (match_operand:HI 1 "h8300_dst_operand" "0,rQ")
(const_int 4))
(match_operand:HI 2 "immediate_operand" "i,i")))]
"TARGET_H8300SX"
"mova/l.l @(%o2,%T1.w),%S0"
[(set_attr "length_table" "mova")
(set_attr "cc" "none")])
 
(define_insn ""
[(set (match_operand:HI 0 "register_operand" "=r,r")
(plus:HI (ashift:HI (match_operand:HI 1 "h8300_dst_operand" "0,rQ")
(const_int 2))
(match_operand:HI 2 "immediate_operand" "i,i")))]
"TARGET_H8300SX"
"mova/l.l @(%o2,%T1.w),%S0"
[(set_attr "length_table" "mova")
(set_attr "cc" "none")])
 
(define_insn ""
[(set (match_operand:SI 0 "register_operand" "=r,r")
(plus:SI (zero_extend:SI (match_operand:HI 1 "h8300_dst_operand" "0,rQ"))
(match_operand:SI 2 "immediate_operand" "i,i")))]
"TARGET_H8300SX"
"mova/b.l @(%o2,%T1.w),%S0"
[(set_attr "length_table" "mova")
(set_attr "cc" "none")])
 
(define_insn ""
[(set (match_operand:SI 0 "register_operand" "=r,r")
(mult:SI (zero_extend:SI (match_operand:HI 1 "h8300_dst_operand" "0,rQ"))
(const_int 2)))]
"TARGET_H8300SX"
"mova/w.l @(0,%T1.w),%S0"
[(set_attr "length_table" "mova_zero")
(set_attr "cc" "none")])
 
(define_insn ""
[(set (match_operand:SI 0 "register_operand" "=r,r")
(plus:SI (mult:SI (zero_extend:SI (match_operand:HI 1 "h8300_dst_operand" "0,rQ"))
(const_int 2))
(match_operand:SI 2 "immediate_operand" "i,i")))]
"TARGET_H8300SX"
"mova/w.l @(%o2,%T1.w),%S0"
[(set_attr "length_table" "mova")
(set_attr "cc" "none")])
 
(define_insn ""
[(set (match_operand:SI 0 "register_operand" "=r")
(and:SI (mult:SI (subreg:SI (match_operand:HI 1 "memory_operand" "m") 0)
(const_int 2))
(const_int 131070)))]
"TARGET_H8300SX"
"mova/w.l @(0,%T1.w),%S0"
[(set_attr "length_table" "mova_zero")
(set_attr "cc" "none")])
 
(define_insn ""
[(set (match_operand:SI 0 "register_operand" "=r")
(plus:SI (and:SI (mult:SI (subreg:SI (match_operand:HI 1 "memory_operand" "m") 0)
(const_int 2))
(const_int 131070))
(match_operand:SI 2 "immediate_operand" "i")))]
"TARGET_H8300SX"
"mova/w.l @(%o2,%T1.w),%S0"
[(set_attr "length_table" "mova")
(set_attr "cc" "none")])
 
(define_insn ""
[(set (match_operand:SI 0 "register_operand" "=r")
(and:SI (mult:SI (match_operand:SI 1 "register_operand" "0")
(const_int 2))
(const_int 131070)))]
"TARGET_H8300SX"
"mova/w.l @(0,%T1.w),%S0"
[(set_attr "length_table" "mova_zero")
(set_attr "cc" "none")])
 
(define_insn ""
[(set (match_operand:SI 0 "register_operand" "=r")
(plus:SI (and:SI (mult:SI (match_operand:SI 1 "register_operand" "0")
(const_int 2))
(const_int 131070))
(match_operand:SI 2 "immediate_operand" "i")))]
"TARGET_H8300SX"
"mova/w.l @(%o2,%T1.w),%S0"
[(set_attr "length_table" "mova")
(set_attr "cc" "none")])
 
(define_insn ""
[(set (match_operand:SI 0 "register_operand" "=r,r")
(ashift:SI (zero_extend:SI (match_operand:HI 1 "h8300_dst_operand" "0,rQ"))
(const_int 1)))]
"TARGET_H8300SX"
"mova/w.l @(0,%T1.w),%S0"
[(set_attr "length_table" "mova_zero")
(set_attr "cc" "none")])
 
(define_insn ""
[(set (match_operand:SI 0 "register_operand" "=r,r")
(plus:SI (ashift:SI (zero_extend:SI (match_operand:HI 1 "h8300_dst_operand" "0,rQ"))
(const_int 1))
(match_operand:SI 2 "immediate_operand" "i,i")))]
"TARGET_H8300SX"
"mova/w.l @(%o2,%T1.w),%S0"
[(set_attr "length_table" "mova")
(set_attr "cc" "none")])
 
(define_insn ""
[(set (match_operand:SI 0 "register_operand" "=r")
(and:SI (ashift:SI (subreg:SI (match_operand:HI 1 "memory_operand" "m") 0)
(const_int 1))
(const_int 131070)))]
"TARGET_H8300SX"
"mova/w.l @(0,%T1.w),%S0"
[(set_attr "length_table" "mova_zero")
(set_attr "cc" "none")])
 
(define_insn ""
[(set (match_operand:SI 0 "register_operand" "=r")
(plus:SI (and:SI (ashift:SI (subreg:SI (match_operand:HI 1 "memory_operand" "m") 0)
(const_int 1))
(const_int 131070))
(match_operand:SI 2 "immediate_operand" "i")))]
"TARGET_H8300SX"
"mova/w.l @(%o2,%T1.w),%S0"
[(set_attr "length_table" "mova")
(set_attr "cc" "none")])
 
(define_insn ""
[(set (match_operand:SI 0 "register_operand" "=r")
(and:SI (ashift:SI (match_operand:SI 1 "register_operand" "0")
(const_int 1))
(const_int 131070)))]
"TARGET_H8300SX"
"mova/w.l @(0,%T1.w),%S0"
[(set_attr "length_table" "mova_zero")
(set_attr "cc" "none")])
 
(define_insn ""
[(set (match_operand:SI 0 "register_operand" "=r")
(plus:SI (and:SI (ashift:SI (match_operand:SI 1 "register_operand" "0")
(const_int 1))
(const_int 131070))
(match_operand:SI 2 "immediate_operand" "i")))]
"TARGET_H8300SX"
"mova/w.l @(%o2,%T1.w),%S0"
[(set_attr "length_table" "mova")
(set_attr "cc" "none")])
 
(define_insn ""
[(set (match_operand:SI 0 "register_operand" "=r,r")
(mult:SI (zero_extend:SI (match_operand:HI 1 "h8300_dst_operand" "0,rQ"))
(const_int 4)))]
"TARGET_H8300SX"
"mova/l.l @(0,%T1.w),%S0"
[(set_attr "length_table" "mova_zero")
(set_attr "cc" "none")])
 
(define_insn ""
[(set (match_operand:SI 0 "register_operand" "=r,r")
(plus:SI (mult:SI (zero_extend:SI (match_operand:HI 1 "h8300_dst_operand" "0,rQ"))
(const_int 4))
(match_operand:SI 2 "immediate_operand" "i,i")))]
"TARGET_H8300SX"
"mova/l.l @(%o2,%T1.w),%S0"
[(set_attr "length_table" "mova")
(set_attr "cc" "none")])
 
(define_insn ""
[(set (match_operand:SI 0 "register_operand" "=r")
(and:SI (mult:SI (subreg:SI (match_operand:HI 1 "memory_operand" "m") 0)
(const_int 4))
(const_int 262140)))]
"TARGET_H8300SX"
"mova/l.l @(0,%T1.w),%S0"
[(set_attr "length_table" "mova_zero")
(set_attr "cc" "none")])
 
(define_insn ""
[(set (match_operand:SI 0 "register_operand" "=r")
(plus:SI (and:SI (mult:SI (subreg:SI (match_operand:HI 1 "memory_operand" "m") 0)
(const_int 4))
(const_int 262140))
(match_operand:SI 2 "immediate_operand" "i")))]
"TARGET_H8300SX"
"mova/l.l @(%o2,%T1.w),%S0"
[(set_attr "length_table" "mova")
(set_attr "cc" "none")])
 
(define_insn ""
[(set (match_operand:SI 0 "register_operand" "=r")
(and:SI (mult:SI (match_operand:SI 1 "register_operand" "0")
(const_int 4))
(const_int 262140)))]
"TARGET_H8300SX"
"mova/l.l @(0,%T1.w),%S0"
[(set_attr "length_table" "mova_zero")
(set_attr "cc" "none")])
 
(define_insn ""
[(set (match_operand:SI 0 "register_operand" "=r")
(plus:SI (and:SI (mult:SI (match_operand:SI 1 "register_operand" "0")
(const_int 4))
(const_int 262140))
(match_operand:SI 2 "immediate_operand" "i")))]
"TARGET_H8300SX"
"mova/l.l @(%o2,%T1.w),%S0"
[(set_attr "length_table" "mova")
(set_attr "cc" "none")])
 
(define_insn ""
[(set (match_operand:SI 0 "register_operand" "=r,r")
(ashift:SI (zero_extend:SI (match_operand:HI 1 "h8300_dst_operand" "0,rQ"))
(const_int 2)))]
"TARGET_H8300SX"
"mova/l.l @(0,%T1.w),%S0"
[(set_attr "length_table" "mova_zero")
(set_attr "cc" "none")])
 
(define_insn ""
[(set (match_operand:SI 0 "register_operand" "=r,r")
(plus:SI (ashift:SI (zero_extend:SI (match_operand:HI 1 "h8300_dst_operand" "0,rQ"))
(const_int 2))
(match_operand:SI 2 "immediate_operand" "i,i")))]
"TARGET_H8300SX"
"mova/l.l @(%o2,%T1.w),%S0"
[(set_attr "length_table" "mova")
(set_attr "cc" "none")])
 
(define_insn ""
[(set (match_operand:SI 0 "register_operand" "=r")
(and:SI (ashift:SI (subreg:SI (match_operand:HI 1 "memory_operand" "m") 0)
(const_int 2))
(const_int 262140)))]
"TARGET_H8300SX"
"mova/l.l @(0,%T1.w),%S0"
[(set_attr "length_table" "mova_zero")
(set_attr "cc" "none")])
 
(define_insn ""
[(set (match_operand:SI 0 "register_operand" "=r")
(plus:SI (and:SI (ashift:SI (subreg:SI (match_operand:HI 1 "memory_operand" "m") 0)
(const_int 2))
(const_int 262140))
(match_operand:SI 2 "immediate_operand" "i")))]
"TARGET_H8300SX"
"mova/l.l @(%o2,%T1.w),%S0"
[(set_attr "length_table" "mova")
(set_attr "cc" "none")])
 
(define_insn ""
[(set (match_operand:SI 0 "register_operand" "=r")
(and:SI (ashift:SI (match_operand:SI 1 "register_operand" "0")
(const_int 2))
(const_int 262140)))]
"TARGET_H8300SX"
"mova/l.l @(0,%T1.w),%S0"
[(set_attr "length_table" "mova_zero")
(set_attr "cc" "none")])
 
(define_insn ""
[(set (match_operand:SI 0 "register_operand" "=r")
(plus:SI (and:SI (ashift:SI (match_operand:SI 1 "register_operand" "0")
(const_int 2))
(const_int 262140))
(match_operand:SI 2 "immediate_operand" "i")))]
"TARGET_H8300SX"
"mova/l.l @(%o2,%T1.w),%S0"
[(set_attr "length_table" "mova")
(set_attr "cc" "none")])
 
/h8300.md
0,0 → 1,6116
;; GCC machine description for Renesas H8/300
;; Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000,
;; 2001, 2002, 2003, 2004, 2005, 2007 Free Software Foundation, Inc.
 
;; Contributed by Steve Chamberlain (sac@cygnus.com),
;; Jim Wilson (wilson@cygnus.com), and Doug Evans (dje@cygnus.com).
 
;; This file is part of GCC.
 
;; GCC is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation; either version 3, or (at your option)
;; any later version.
 
;; GCC is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
 
;; You should have received a copy of the GNU General Public License
;; along with GCC; see the file COPYING3. If not see
;; <http://www.gnu.org/licenses/>.
 
;; We compute exact length on each instruction for most of the time.
;; In some case, most notably bit operations that may involve memory
;; operands, the lengths in this file are "worst case".
 
;; On the H8/300H and H8S, adds/subs operate on the 32bit "er"
;; registers. Right now GCC doesn't expose the "e" half to the
;; compiler, so using add/subs for addhi and subhi is safe. Long
;; term, we want to expose the "e" half to the compiler (gives us 8
;; more 16bit registers). At that point addhi and subhi can't use
;; adds/subs.
 
;; There's currently no way to have an insv/extzv expander for the H8/300H
;; because word_mode is different for the H8/300 and H8/300H.
 
;; Shifts/rotates by small constants should be handled by special
;; patterns so we get the length and cc status correct.
 
;; Bitfield operations no longer accept memory operands. We need
;; to add variants which operate on memory back to the MD.
 
;; ??? Implement remaining bit ops available on the h8300
 
;; ----------------------------------------------------------------------
;; CONSTANTS
;; ----------------------------------------------------------------------
 
(define_constants
[(UNSPEC_INCDEC 0)
(UNSPEC_MONITOR 1)])
 
(define_constants
[(UNSPEC_MOVMD 100)
(UNSPEC_STPCPY 101)])
 
(define_constants
[(R0_REG 0)
(SC_REG 3)
(COUNTER_REG 4)
(SOURCE_REG 5)
(DESTINATION_REG 6)
(HFP_REG 6)
(SP_REG 7)
(MAC_REG 8)
(AP_REG 9)
(RAP_REG 10)
(FP_REG 11)])
 
;; ----------------------------------------------------------------------
;; ATTRIBUTES
;; ----------------------------------------------------------------------
 
(define_attr "cpu" "h8300,h8300h"
(const (symbol_ref "cpu_type")))
 
(define_attr "type" "branch,arith,bitbranch,call"
(const_string "arith"))
 
(define_attr "length_table" "none,addb,addw,addl,logicb,movb,movw,movl,mova_zero,mova,unary,mov_imm4,short_immediate,bitfield,bitbranch"
(const_string "none"))
 
;; The size of instructions in bytes.
 
(define_attr "length" ""
(cond [(eq_attr "type" "branch")
;; In a forward delayed branch, (pc) represents the end of the
;; delay sequence, not the end of the branch itself.
(if_then_else (and (ge (minus (match_dup 0) (pc))
(const_int -126))
(le (plus (minus (match_dup 0) (pc))
(symbol_ref "DELAY_SLOT_LENGTH (insn)"))
(const_int 126)))
(const_int 2)
(if_then_else (and (eq_attr "cpu" "h8300h")
(and (ge (minus (pc) (match_dup 0))
(const_int -32000))
(le (minus (pc) (match_dup 0))
(const_int 32000))))
(const_int 4)
(const_int 6)))
(eq_attr "type" "bitbranch")
(if_then_else
(and (ge (minus (match_dup 0) (pc))
(const_int -126))
(le (minus (match_dup 0) (pc))
(const_int 126)))
(plus
(symbol_ref "h8300_insn_length_from_table (insn, operands)")
(const_int 2))
(if_then_else
(and (eq_attr "cpu" "h8300h")
(and (ge (minus (pc) (match_dup 0))
(const_int -32000))
(le (minus (pc) (match_dup 0))
(const_int 32000))))
(plus
(symbol_ref "h8300_insn_length_from_table (insn, operands)")
(const_int 4))
(plus
(symbol_ref "h8300_insn_length_from_table (insn, operands)")
(const_int 6))))
(eq_attr "length_table" "!none")
(symbol_ref "h8300_insn_length_from_table (insn, operands)")]
(const_int 200)))
 
;; Condition code settings.
;;
;; none - insn does not affect cc
;; none_0hit - insn does not affect cc but it does modify operand 0
;; This attribute is used to keep track of when operand 0 changes.
;; See the description of NOTICE_UPDATE_CC for more info.
;; set_znv - insn sets z,n,v to usable values (like a tst insn); c is unknown.
;; set_zn - insn sets z,n to usable values; v,c are unknown.
;; compare - compare instruction
;; clobber - value of cc is unknown
 
(define_attr "cc" "none,none_0hit,set_znv,set_zn,compare,clobber"
(const_string "clobber"))
 
;; Type of delay slot. NONE means the instruction has no delay slot.
;; JUMP means it is an unconditional jump that (if short enough)
;; could be implemented using bra/s.
(define_attr "delay_slot" "none,jump"
(const_string "none"))
 
;; "yes" if the instruction can be put into a delay slot. It's not
;; entirely clear that jsr is not valid in delay slots, but it
;; definitely doesn't have the effect of causing the called function
;; to return to the target of the delayed branch.
(define_attr "can_delay" "no,yes"
(cond [(eq_attr "type" "branch,bitbranch,call")
(const_string "no")
(ne (symbol_ref "get_attr_length (insn)") (const_int 2))
(const_string "no")]
(const_string "yes")))
 
;; Only allow jumps to have a delay slot if we think they might
;; be short enough. This is just an optimization: we don't know
;; for certain whether they will be or not.
(define_delay (and (eq_attr "delay_slot" "jump")
(eq (symbol_ref "get_attr_length (insn)") (const_int 2)))
[(eq_attr "can_delay" "yes")
(nil)
(nil)])
 
;; Provide the maximum length of an assembly instruction in an asm
;; statement. The maximum length of 14 bytes is achieved on H8SX.
 
(define_asm_attributes
[(set (attr "length")
(cond [(ne (symbol_ref "TARGET_H8300") (const_int 0)) (const_int 4)
(ne (symbol_ref "TARGET_H8300H") (const_int 0)) (const_int 10)
(ne (symbol_ref "TARGET_H8300S") (const_int 0)) (const_int 10)]
(const_int 14)))])
 
(include "predicates.md")
;; ----------------------------------------------------------------------
;; MOVE INSTRUCTIONS
;; ----------------------------------------------------------------------
 
;; movqi
 
(define_insn "*movqi_h8300"
[(set (match_operand:QI 0 "general_operand_dst" "=r,r ,<,r,r,m")
(match_operand:QI 1 "general_operand_src" " I,r>,r,n,m,r"))]
"TARGET_H8300
&& (register_operand (operands[0], QImode)
|| register_operand (operands[1], QImode))"
"@
sub.b %X0,%X0
mov.b %R1,%X0
mov.b %X1,%R0
mov.b %R1,%X0
mov.b %R1,%X0
mov.b %X1,%R0"
[(set_attr "length" "2,2,2,2,4,4")
(set_attr "cc" "set_zn,set_znv,set_znv,set_znv,set_znv,set_znv")])
 
(define_insn "*movqi_h8300hs"
[(set (match_operand:QI 0 "general_operand_dst" "=r,r ,<,r,r,m")
(match_operand:QI 1 "general_operand_src" " I,r>,r,n,m,r"))]
"(TARGET_H8300H || TARGET_H8300S) && !TARGET_H8300SX
&& (register_operand (operands[0], QImode)
|| register_operand (operands[1], QImode))"
"@
sub.b %X0,%X0
mov.b %R1,%X0
mov.b %X1,%R0
mov.b %R1,%X0
mov.b %R1,%X0
mov.b %X1,%R0"
[(set (attr "length")
(symbol_ref "compute_mov_length (operands)"))
(set_attr "cc" "set_zn,set_znv,set_znv,clobber,set_znv,set_znv")])
 
(define_insn "*movqi_h8sx"
[(set (match_operand:QI 0 "general_operand_dst" "=Z,rQ")
(match_operand:QI 1 "general_operand_src" "P4>X,rQi"))]
"TARGET_H8300SX"
"@
mov.b %X1,%X0
mov.b %X1,%X0"
[(set_attr "length_table" "mov_imm4,movb")
(set_attr "cc" "set_znv")])
 
(define_expand "movqi"
[(set (match_operand:QI 0 "general_operand_dst" "")
(match_operand:QI 1 "general_operand_src" ""))]
""
"
{
/* One of the ops has to be in a register. */
if (!TARGET_H8300SX
&& !register_operand (operand0, QImode)
&& !register_operand (operand1, QImode))
{
operands[1] = copy_to_mode_reg (QImode, operand1);
}
}")
 
(define_insn "movstrictqi"
[(set (strict_low_part (match_operand:QI 0 "general_operand_dst" "+r,r"))
(match_operand:QI 1 "general_operand_src" "I,rmi>"))]
""
"@
sub.b %X0,%X0
mov.b %X1,%X0"
[(set_attr "length" "2,*")
(set_attr "length_table" "*,movb")
(set_attr "cc" "set_zn,set_znv")])
 
;; movhi
 
(define_insn "*movhi_h8300"
[(set (match_operand:HI 0 "general_operand_dst" "=r,r,<,r,r,m")
(match_operand:HI 1 "general_operand_src" "I,r>,r,i,m,r"))]
"TARGET_H8300
&& (register_operand (operands[0], HImode)
|| register_operand (operands[1], HImode))
&& !(GET_CODE (operands[0]) == MEM
&& GET_CODE (XEXP (operands[0], 0)) == PRE_DEC
&& GET_CODE (XEXP (XEXP (operands[0], 0), 0)) == REG
&& GET_CODE (operands[1]) == REG
&& REGNO (XEXP (XEXP (operands[0], 0), 0)) == REGNO (operands[1]))"
"@
sub.w %T0,%T0
mov.w %T1,%T0
mov.w %T1,%T0
mov.w %T1,%T0
mov.w %T1,%T0
mov.w %T1,%T0"
[(set (attr "length")
(symbol_ref "compute_mov_length (operands)"))
(set_attr "cc" "set_zn,set_znv,set_znv,set_znv,set_znv,set_znv")])
 
(define_insn "*movhi_h8300hs"
[(set (match_operand:HI 0 "general_operand_dst" "=r,r,<,r,r,m")
(match_operand:HI 1 "general_operand_src" "I,r>,r,i,m,r"))]
"(TARGET_H8300H || TARGET_H8300S) && !TARGET_H8300SX
&& (register_operand (operands[0], HImode)
|| register_operand (operands[1], HImode))"
"@
sub.w %T0,%T0
mov.w %T1,%T0
mov.w %T1,%T0
mov.w %T1,%T0
mov.w %T1,%T0
mov.w %T1,%T0"
[(set (attr "length")
(symbol_ref "compute_mov_length (operands)"))
(set_attr "cc" "set_zn,set_znv,set_znv,set_znv,set_znv,set_znv")])
 
(define_insn "*movhi_h8sx"
[(set (match_operand:HI 0 "general_operand_dst" "=r,r,Z,Q,rQ")
(match_operand:HI 1 "general_operand_src" "I,P3>X,P4>X,IP8>X,rQi"))]
"TARGET_H8300SX"
"@
sub.w %T0,%T0
mov.w %T1,%T0
mov.w %T1,%T0
mov.w %T1,%T0
mov.w %T1,%T0"
[(set_attr "length_table" "*,*,mov_imm4,short_immediate,movw")
(set_attr "length" "2,2,*,*,*")
(set_attr "cc" "set_zn,set_znv,set_znv,set_znv,set_znv")])
 
(define_expand "movhi"
[(set (match_operand:HI 0 "general_operand_dst" "")
(match_operand:HI 1 "general_operand_src" ""))]
""
"
{
/* One of the ops has to be in a register. */
if (!register_operand (operand1, HImode)
&& !register_operand (operand0, HImode))
{
operands[1] = copy_to_mode_reg (HImode, operand1);
}
}")
 
(define_insn "movstricthi"
[(set (strict_low_part (match_operand:HI 0 "general_operand_dst" "+r,r,r"))
(match_operand:HI 1 "general_operand_src" "I,P3>X,rmi"))]
""
"@
sub.w %T0,%T0
mov.w %T1,%T0
mov.w %T1,%T0"
[(set_attr "length" "2,2,*")
(set_attr "length_table" "*,*,movw")
(set_attr "cc" "set_zn,set_znv,set_znv")])
 
;; movsi
 
(define_expand "movsi"
[(set (match_operand:SI 0 "general_operand_dst" "")
(match_operand:SI 1 "general_operand_src" ""))]
""
"
{
if (TARGET_H8300)
{
if (h8300_expand_movsi (operands))
DONE;
}
else if (!TARGET_H8300SX)
{
/* One of the ops has to be in a register. */
if (!register_operand (operand1, SImode)
&& !register_operand (operand0, SImode))
{
operands[1] = copy_to_mode_reg (SImode, operand1);
}
}
}")
 
(define_insn "*movsi_h8300"
[(set (match_operand:SI 0 "general_operand_dst" "=r,r,r,o,<,r")
(match_operand:SI 1 "general_operand_src" "I,r,io,r,r,>"))]
"TARGET_H8300
&& (register_operand (operands[0], SImode)
|| register_operand (operands[1], SImode))"
"*
{
unsigned int rn = -1;
switch (which_alternative)
{
case 0:
return \"sub.w %e0,%e0\;sub.w %f0,%f0\";
case 1:
if (REGNO (operands[0]) < REGNO (operands[1]))
return \"mov.w %e1,%e0\;mov.w %f1,%f0\";
else
return \"mov.w %f1,%f0\;mov.w %e1,%e0\";
case 2:
/* Make sure we don't trample the register we index with. */
if (GET_CODE (operands[1]) == MEM)
{
rtx inside = XEXP (operands[1], 0);
if (REG_P (inside))
{
rn = REGNO (inside);
}
else if (GET_CODE (inside) == PLUS)
{
rtx lhs = XEXP (inside, 0);
rtx rhs = XEXP (inside, 1);
if (REG_P (lhs)) rn = REGNO (lhs);
if (REG_P (rhs)) rn = REGNO (rhs);
}
}
if (rn == REGNO (operands[0]))
{
/* Move the second word first. */
return \"mov.w %f1,%f0\;mov.w %e1,%e0\";
}
else
{
if (GET_CODE (operands[1]) == CONST_INT)
{
/* If either half is zero, use sub.w to clear that
half. */
if ((INTVAL (operands[1]) & 0xffff) == 0)
return \"mov.w %e1,%e0\;sub.w %f0,%f0\";
if (((INTVAL (operands[1]) >> 16) & 0xffff) == 0)
return \"sub.w %e0,%e0\;mov.w %f1,%f0\";
/* If the upper half and the lower half are the same,
copy one half to the other. */
if ((INTVAL (operands[1]) & 0xffff)
== ((INTVAL (operands[1]) >> 16) & 0xffff))
return \"mov.w\\t%e1,%e0\;mov.w\\t%e0,%f0\";
}
return \"mov.w %e1,%e0\;mov.w %f1,%f0\";
}
case 3:
return \"mov.w %e1,%e0\;mov.w %f1,%f0\";
case 4:
return \"mov.w %f1,%T0\;mov.w %e1,%T0\";
case 5:
return \"mov.w %T1,%e0\;mov.w %T1,%f0\";
default:
gcc_unreachable ();
}
}"
[(set (attr "length")
(symbol_ref "compute_mov_length (operands)"))])
 
(define_insn "*movsi_h8300hs"
[(set (match_operand:SI 0 "general_operand_dst" "=r,r,r,<,r,r,m,*a,*a,r")
(match_operand:SI 1 "general_operand_src" "I,r,i,r,>,m,r,I,r,*a"))]
"(TARGET_H8300S || TARGET_H8300H) && !TARGET_H8300SX
&& (register_operand (operands[0], SImode)
|| register_operand (operands[1], SImode))
&& !(GET_CODE (operands[0]) == MEM
&& GET_CODE (XEXP (operands[0], 0)) == PRE_DEC
&& GET_CODE (XEXP (XEXP (operands[0], 0), 0)) == REG
&& GET_CODE (operands[1]) == REG
&& REGNO (XEXP (XEXP (operands[0], 0), 0)) == REGNO (operands[1]))"
"*
{
switch (which_alternative)
{
case 0:
return \"sub.l %S0,%S0\";
case 7:
return \"clrmac\";
case 8:
return \"clrmac\;ldmac %1,macl\";
case 9:
return \"stmac macl,%0\";
default:
if (GET_CODE (operands[1]) == CONST_INT)
{
int val = INTVAL (operands[1]);
 
/* Look for constants which can be made by adding an 8-bit
number to zero in one of the two low bytes. */
if (val == (val & 0xff))
{
operands[1] = GEN_INT ((char) val & 0xff);
return \"sub.l\\t%S0,%S0\;add.b\\t%1,%w0\";
}
 
if (val == (val & 0xff00))
{
operands[1] = GEN_INT ((char) (val >> 8) & 0xff);
return \"sub.l\\t%S0,%S0\;add.b\\t%1,%x0\";
}
 
/* Look for constants that can be obtained by subs, inc, and
dec to 0. */
switch (val & 0xffffffff)
{
case 0xffffffff:
return \"sub.l\\t%S0,%S0\;subs\\t#1,%S0\";
case 0xfffffffe:
return \"sub.l\\t%S0,%S0\;subs\\t#2,%S0\";
case 0xfffffffc:
return \"sub.l\\t%S0,%S0\;subs\\t#4,%S0\";
 
case 0x0000ffff:
return \"sub.l\\t%S0,%S0\;dec.w\\t#1,%f0\";
case 0x0000fffe:
return \"sub.l\\t%S0,%S0\;dec.w\\t#2,%f0\";
 
case 0xffff0000:
return \"sub.l\\t%S0,%S0\;dec.w\\t#1,%e0\";
case 0xfffe0000:
return \"sub.l\\t%S0,%S0\;dec.w\\t#2,%e0\";
 
case 0x00010000:
return \"sub.l\\t%S0,%S0\;inc.w\\t#1,%e0\";
case 0x00020000:
return \"sub.l\\t%S0,%S0\;inc.w\\t#2,%e0\";
}
}
}
return \"mov.l %S1,%S0\";
}"
[(set (attr "length")
(symbol_ref "compute_mov_length (operands)"))
(set_attr "cc" "set_zn,set_znv,clobber,set_znv,set_znv,set_znv,set_znv,none_0hit,none_0hit,set_znv")])
 
(define_insn "*movsi_h8sx"
[(set (match_operand:SI 0 "general_operand_dst" "=r,r,Q,rQ,*a,*a,r")
(match_operand:SI 1 "general_operand_src" "I,P3>X,IP8>X,rQi,I,r,*a"))]
"TARGET_H8300SX"
"@
sub.l %S0,%S0
mov.l %S1,%S0
mov.l %S1,%S0
mov.l %S1,%S0
clrmac
clrmac\;ldmac %1,macl
stmac macl,%0"
[(set_attr "length_table" "*,*,short_immediate,movl,*,*,*")
(set_attr "length" "2,2,*,*,2,6,4")
(set_attr "cc" "set_zn,set_znv,set_znv,set_znv,none_0hit,none_0hit,set_znv")])
 
(define_insn "*movsf_h8sx"
[(set (match_operand:SF 0 "general_operand_dst" "=r,rQ")
(match_operand:SF 1 "general_operand_src" "G,rQi"))]
"TARGET_H8300SX"
"@
sub.l %S0,%S0
mov.l %S1,%S0"
[(set_attr "length" "2,*")
(set_attr "length_table" "*,movl")
(set_attr "cc" "set_zn,set_znv")])
 
;; Implement block moves using movmd. Defining movmemsi allows the full
;; range of constant lengths (up to 0x40000 bytes when using movmd.l).
;; See h8sx_emit_movmd for details.
(define_expand "movmemsi"
[(use (match_operand:BLK 0 "memory_operand" ""))
(use (match_operand:BLK 1 "memory_operand" ""))
(use (match_operand:SI 2 "" ""))
(use (match_operand:SI 3 "const_int_operand" ""))]
"TARGET_H8300SX"
{
if (h8sx_emit_movmd (operands[0], operands[1], operands[2],
INTVAL (operands[3])))
DONE;
else
FAIL;
})
 
;; Expander for generating movmd insns. Operand 0 is the destination
;; memory region, operand 1 is the source, operand 2 is the counter
;; register and operand 3 is the chunk size (1, 2 or 4).
(define_expand "movmd"
[(parallel
[(set (match_operand:BLK 0 "memory_operand" "")
(match_operand:BLK 1 "memory_operand" ""))
(unspec [(match_operand:HI 2 "register_operand" "")
(match_operand:HI 3 "const_int_operand" "")] UNSPEC_MOVMD)
(clobber (match_dup 4))
(clobber (match_dup 5))
(set (match_dup 2)
(const_int 0))])]
"TARGET_H8300SX"
{
operands[4] = copy_rtx (XEXP (operands[0], 0));
operands[5] = copy_rtx (XEXP (operands[1], 0));
})
 
 
;; This is a difficult instruction to reload since operand 0 must be the
;; frame pointer. See h8300_reg_class_from_letter for an explanation.
(define_insn "movmd_internal_normal"
[(set (mem:BLK (match_operand:HI 3 "register_operand" "0,r"))
(mem:BLK (match_operand:HI 4 "register_operand" "1,1")))
(unspec [(match_operand:HI 5 "register_operand" "2,2")
(match_operand:HI 6 "const_int_operand" "n,n")] UNSPEC_MOVMD)
(clobber (match_operand:HI 0 "register_operand" "=d,??D"))
(clobber (match_operand:HI 1 "register_operand" "=f,f"))
(set (match_operand:HI 2 "register_operand" "=c,c")
(const_int 0))]
"TARGET_H8300SX && TARGET_NORMAL_MODE"
"@
movmd%m6
#"
[(set_attr "length" "2,14")
(set_attr "can_delay" "no")
(set_attr "cc" "none,clobber")])
 
(define_insn "movmd_internal"
[(set (mem:BLK (match_operand:SI 3 "register_operand" "0,r"))
(mem:BLK (match_operand:SI 4 "register_operand" "1,1")))
(unspec [(match_operand:HI 5 "register_operand" "2,2")
(match_operand:HI 6 "const_int_operand" "n,n")] UNSPEC_MOVMD)
(clobber (match_operand:SI 0 "register_operand" "=d,??D"))
(clobber (match_operand:SI 1 "register_operand" "=f,f"))
(set (match_operand:HI 2 "register_operand" "=c,c")
(const_int 0))]
"TARGET_H8300SX && !TARGET_NORMAL_MODE"
"@
movmd%m6
#"
[(set_attr "length" "2,14")
(set_attr "can_delay" "no")
(set_attr "cc" "none,clobber")])
 
;; Split the above instruction if the destination register isn't er6.
;; We need a sequence like:
;;
;; mov.l er6,@-er7
;; mov.l <dest>,er6
;; movmd.sz
;; mov.l er6,<dest>
;; mov.l @er7+,er6
;;
;; where <dest> is the current destination register (operand 4).
;; The fourth instruction will be deleted if <dest> dies here.
(define_split
[(set (match_operand:BLK 0 "memory_operand" "")
(match_operand:BLK 1 "memory_operand" ""))
(unspec [(match_operand:HI 2 "register_operand" "")
(match_operand:HI 3 "const_int_operand" "")] UNSPEC_MOVMD)
(clobber (match_operand:HI 4 "register_operand" ""))
(clobber (match_operand:HI 5 "register_operand" ""))
(set (match_dup 2)
(const_int 0))]
"TARGET_H8300SX && TARGET_NORMAL_MODE
&& reload_completed
&& REGNO (operands[4]) != DESTINATION_REG"
[(const_int 0)]
{
rtx dest;
 
h8300_swap_into_er6 (XEXP (operands[0], 0));
dest = replace_equiv_address (operands[0], hard_frame_pointer_rtx);
emit_insn (gen_movmd (dest, operands[1], operands[2], operands[3]));
h8300_swap_out_of_er6 (operands[4]);
DONE;
})
 
(define_split
[(set (match_operand:BLK 0 "memory_operand" "")
(match_operand:BLK 1 "memory_operand" ""))
(unspec [(match_operand:HI 2 "register_operand" "")
(match_operand:HI 3 "const_int_operand" "")] UNSPEC_MOVMD)
(clobber (match_operand:SI 4 "register_operand" ""))
(clobber (match_operand:SI 5 "register_operand" ""))
(set (match_dup 2)
(const_int 0))]
"TARGET_H8300SX && !TARGET_NORMAL_MODE
&& reload_completed
&& REGNO (operands[4]) != DESTINATION_REG"
[(const_int 0)]
{
rtx dest;
 
h8300_swap_into_er6 (XEXP (operands[0], 0));
dest = replace_equiv_address (operands[0], hard_frame_pointer_rtx);
emit_insn (gen_movmd (dest, operands[1], operands[2], operands[3]));
h8300_swap_out_of_er6 (operands[4]);
DONE;
})
 
;; Expand a call to stpcpy() using movsd. Operand 0 should point to
;; the final character, but movsd leaves it pointing to the character
;; after that.
(define_expand "movstr"
[(use (match_operand 0 "register_operand" ""))
(use (match_operand:BLK 1 "memory_operand" ""))
(use (match_operand:BLK 2 "memory_operand" ""))]
"TARGET_H8300SX"
{
operands[1] = replace_equiv_address
(operands[1], copy_to_mode_reg (Pmode, XEXP (operands[1], 0)));
operands[2] = replace_equiv_address
(operands[2], copy_to_mode_reg (Pmode, XEXP (operands[2], 0)));
emit_insn (gen_movsd (operands[1], operands[2], gen_reg_rtx (Pmode)));
emit_insn (gen_add3_insn (operands[0],
XEXP (operands[1], 0),
constm1_rtx));
DONE;
})
 
;; Expander for generating a movsd instruction. Operand 0 is the
;; destination string, operand 1 is the source string and operand 2
;; is a scratch register.
(define_expand "movsd"
[(parallel
[(set (match_operand:BLK 0 "memory_operand" "")
(unspec:BLK [(match_operand:BLK 1 "memory_operand" "")]
UNSPEC_STPCPY))
(clobber (match_dup 3))
(clobber (match_dup 4))
(clobber (match_operand 2 "register_operand" ""))])]
"TARGET_H8300SX"
{
operands[3] = copy_rtx (XEXP (operands[0], 0));
operands[4] = copy_rtx (XEXP (operands[1], 0));
})
 
;; See comments above memcpy_internal().
(define_insn "stpcpy_internal_normal"
[(set (mem:BLK (match_operand:HI 3 "register_operand" "0,r"))
(unspec:BLK [(mem:BLK (match_operand:HI 4 "register_operand" "1,1"))]
UNSPEC_STPCPY))
(clobber (match_operand:HI 0 "register_operand" "=d,??D"))
(clobber (match_operand:HI 1 "register_operand" "=f,f"))
(clobber (match_operand:HI 2 "register_operand" "=c,c"))]
"TARGET_H8300SX && TARGET_NORMAL_MODE"
"@
\n1:\tmovsd\t2f\;bra\t1b\n2:
#"
[(set_attr "length" "6,18")
(set_attr "cc" "none,clobber")])
 
(define_insn "stpcpy_internal"
[(set (mem:BLK (match_operand:SI 3 "register_operand" "0,r"))
(unspec:BLK [(mem:BLK (match_operand:SI 4 "register_operand" "1,1"))]
UNSPEC_STPCPY))
(clobber (match_operand:SI 0 "register_operand" "=d,??D"))
(clobber (match_operand:SI 1 "register_operand" "=f,f"))
(clobber (match_operand:SI 2 "register_operand" "=c,c"))]
"TARGET_H8300SX && !TARGET_NORMAL_MODE"
"@
\n1:\tmovsd\t2f\;bra\t1b\n2:
#"
[(set_attr "length" "6,18")
(set_attr "cc" "none,clobber")])
 
;; Split the above instruction if the destination isn't er6. This works
;; in the same way as the movmd splitter.
(define_split
[(set (match_operand:BLK 0 "memory_operand" "")
(unspec:BLK [(match_operand:BLK 1 "memory_operand" "")] UNSPEC_STPCPY))
(clobber (match_operand:HI 2 "register_operand" ""))
(clobber (match_operand:HI 3 "register_operand" ""))
(clobber (match_operand:HI 4 "register_operand" ""))]
"TARGET_H8300SX && TARGET_NORMAL_MODE
&& reload_completed
&& REGNO (operands[2]) != DESTINATION_REG"
[(const_int 0)]
{
rtx dest;
 
h8300_swap_into_er6 (XEXP (operands[0], 0));
dest = replace_equiv_address (operands[0], hard_frame_pointer_rtx);
emit_insn (gen_movsd (dest, operands[1], operands[4]));
h8300_swap_out_of_er6 (operands[2]);
DONE;
})
 
(define_split
[(set (match_operand:BLK 0 "memory_operand" "")
(unspec:BLK [(match_operand:BLK 1 "memory_operand" "")] UNSPEC_STPCPY))
(clobber (match_operand:SI 2 "register_operand" ""))
(clobber (match_operand:SI 3 "register_operand" ""))
(clobber (match_operand:SI 4 "register_operand" ""))]
"TARGET_H8300SX && !TARGET_NORMAL_MODE
&& reload_completed
&& REGNO (operands[2]) != DESTINATION_REG"
[(const_int 0)]
{
rtx dest;
 
h8300_swap_into_er6 (XEXP (operands[0], 0));
dest = replace_equiv_address (operands[0], hard_frame_pointer_rtx);
emit_insn (gen_movsd (dest, operands[1], operands[4]));
h8300_swap_out_of_er6 (operands[2]);
DONE;
})
 
(include "mova.md")
 
(define_expand "movsf"
[(set (match_operand:SF 0 "general_operand_dst" "")
(match_operand:SF 1 "general_operand_src" ""))]
""
"
{
if (TARGET_H8300)
{
if (h8300_expand_movsi (operands))
DONE;
}
else if (!TARGET_H8300SX)
{
/* One of the ops has to be in a register. */
if (!register_operand (operand1, SFmode)
&& !register_operand (operand0, SFmode))
{
operands[1] = copy_to_mode_reg (SFmode, operand1);
}
}
}")
 
(define_insn "*movsf_h8300"
[(set (match_operand:SF 0 "general_operand_dst" "=r,r,r,o,<,r")
(match_operand:SF 1 "general_operand_src" "G,r,io,r,r,>"))]
"TARGET_H8300
&& (register_operand (operands[0], SFmode)
|| register_operand (operands[1], SFmode))"
"*
{
/* Copy of the movsi stuff. */
unsigned int rn = -1;
switch (which_alternative)
{
case 0:
return \"sub.w %e0,%e0\;sub.w %f0,%f0\";
case 1:
if (REGNO (operands[0]) < REGNO (operands[1]))
return \"mov.w %e1,%e0\;mov.w %f1,%f0\";
else
return \"mov.w %f1,%f0\;mov.w %e1,%e0\";
case 2:
/* Make sure we don't trample the register we index with. */
if (GET_CODE (operands[1]) == MEM)
{
rtx inside = XEXP (operands[1], 0);
if (REG_P (inside))
{
rn = REGNO (inside);
}
else if (GET_CODE (inside) == PLUS)
{
rtx lhs = XEXP (inside, 0);
rtx rhs = XEXP (inside, 1);
if (REG_P (lhs)) rn = REGNO (lhs);
if (REG_P (rhs)) rn = REGNO (rhs);
}
}
if (rn == REGNO (operands[0]))
/* Move the second word first. */
return \"mov.w %f1,%f0\;mov.w %e1,%e0\";
else
/* Move the first word first. */
return \"mov.w %e1,%e0\;mov.w %f1,%f0\";
 
case 3:
return \"mov.w %e1,%e0\;mov.w %f1,%f0\";
case 4:
return \"mov.w %f1,%T0\;mov.w %e1,%T0\";
case 5:
return \"mov.w %T1,%e0\;mov.w %T1,%f0\";
default:
gcc_unreachable ();
}
}"
[(set (attr "length")
(symbol_ref "compute_mov_length (operands)"))])
 
(define_insn "*movsf_h8300hs"
[(set (match_operand:SF 0 "general_operand_dst" "=r,r,r,m,<,r")
(match_operand:SF 1 "general_operand_src" "G,r,im,r,r,>"))]
"(TARGET_H8300H || TARGET_H8300S) && !TARGET_H8300SX
&& (register_operand (operands[0], SFmode)
|| register_operand (operands[1], SFmode))"
"@
sub.l %S0,%S0
mov.l %S1,%S0
mov.l %S1,%S0
mov.l %S1,%S0
mov.l %S1,%S0
mov.l %S1,%S0"
[(set (attr "length")
(symbol_ref "compute_mov_length (operands)"))
(set_attr "cc" "set_zn,set_znv,set_znv,set_znv,set_znv,set_znv")])
;; ----------------------------------------------------------------------
;; PUSH INSTRUCTIONS
;; ----------------------------------------------------------------------
 
(define_insn "pushqi1_h8300"
[(set (reg:HI SP_REG)
(plus:HI (reg:HI SP_REG) (const_int -2)))
(set (mem:QI (plus:HI (reg:HI SP_REG) (const_int -1)))
(match_operand:QI 0 "register_operand" "r"))]
"TARGET_H8300
&& operands[0] != stack_pointer_rtx"
"mov.w\\t%T0,@-r7"
[(set_attr "length" "2")])
 
(define_insn "pushqi1_h8300hs_advanced"
[(set (reg:SI SP_REG)
(plus:SI (reg:SI SP_REG) (const_int -4)))
(set (mem:QI (plus:SI (reg:SI SP_REG) (const_int -3)))
(match_operand:QI 0 "register_operand" "r"))]
"(TARGET_H8300H || TARGET_H8300S)
&& operands[0] != stack_pointer_rtx"
"mov.l\\t%S0,@-er7"
[(set_attr "length" "4")])
 
(define_insn "pushqi1_h8300hs_normal"
[(set (reg:HI SP_REG)
(plus:HI (reg:HI SP_REG) (const_int -4)))
(set (mem:QI (plus:HI (reg:HI SP_REG) (const_int -3)))
(match_operand:QI 0 "register_operand" "r"))]
"(TARGET_H8300H || TARGET_H8300S)
&& operands[0] != stack_pointer_rtx"
"mov.l\\t%S0,@-er7"
[(set_attr "length" "4")])
 
(define_expand "pushqi1"
[(match_operand:QI 0 "register_operand" "")]
""
"
{
if (TARGET_H8300)
emit_insn (gen_pushqi1_h8300 (operands[0]));
else if (!TARGET_NORMAL_MODE)
emit_insn (gen_pushqi1_h8300hs_advanced (operands[0]));
else
emit_insn (gen_pushqi1_h8300hs_normal (operands[0]));
DONE;
}")
 
(define_expand "pushhi1_h8300"
[(set (mem:HI (pre_dec:HI (reg:HI SP_REG)))
(match_operand:HI 0 "register_operand" ""))]
"TARGET_H8300
&& operands[0] != stack_pointer_rtx"
"")
 
(define_insn "pushhi1_h8300hs_advanced"
[(set (reg:SI SP_REG)
(plus:SI (reg:SI SP_REG) (const_int -4)))
(set (mem:HI (plus:SI (reg:SI SP_REG) (const_int -2)))
(match_operand:HI 0 "register_operand" "r"))]
"(TARGET_H8300H || TARGET_H8300S)
&& operands[0] != stack_pointer_rtx"
"mov.l\\t%S0,@-er7"
[(set_attr "length" "4")])
 
(define_insn "pushhi1_h8300hs_normal"
[(set (reg:HI SP_REG)
(plus:HI (reg:HI SP_REG) (const_int -4)))
(set (mem:HI (plus:HI (reg:HI SP_REG) (const_int -2)))
(match_operand:HI 0 "register_operand" "r"))]
"(TARGET_H8300H || TARGET_H8300S)
&& operands[0] != stack_pointer_rtx"
"mov.l\\t%S0,@-er7"
[(set_attr "length" "4")])
 
(define_expand "pushhi1"
[(match_operand:HI 0 "register_operand" "")]
""
"
{
if (TARGET_H8300)
emit_insn (gen_pushhi1_h8300 (operands[0]));
else if (!TARGET_NORMAL_MODE)
emit_insn (gen_pushhi1_h8300hs_advanced (operands[0]));
else
emit_insn (gen_pushhi1_h8300hs_normal (operands[0]));
DONE;
}")
;; ----------------------------------------------------------------------
;; TEST INSTRUCTIONS
;; ----------------------------------------------------------------------
 
(define_insn ""
[(set (cc0) (zero_extract:HI (match_operand:QI 0 "bit_memory_operand" "r,U")
(const_int 1)
(match_operand 1 "const_int_operand" "n,n")))]
"TARGET_H8300"
"btst %Z1,%Y0"
[(set_attr "length" "2,4")
(set_attr "cc" "set_zn,set_zn")])
 
(define_insn ""
[(set (cc0) (zero_extract:HI (match_operand:HI 0 "register_operand" "r")
(const_int 1)
(match_operand 1 "const_int_operand" "n")))]
"TARGET_H8300"
"btst %Z1,%Y0"
[(set_attr "length" "2")
(set_attr "cc" "set_zn")])
 
(define_insn_and_split "*tst_extzv_1_n"
[(set (cc0)
(zero_extract:SI (match_operand:QI 0 "general_operand_src" "r,U,mn>")
(const_int 1)
(match_operand 1 "const_int_operand" "n,n,n")))
(clobber (match_scratch:QI 2 "=X,X,&r"))]
"(TARGET_H8300H || TARGET_H8300S)"
"@
btst\\t%Z1,%Y0
btst\\t%Z1,%Y0
#"
"&& reload_completed
&& !OK_FOR_U (operands[0])"
[(set (match_dup 2)
(match_dup 0))
(parallel [(set (cc0) (zero_extract:SI (match_dup 2)
(const_int 1)
(match_dup 1)))
(clobber (scratch:QI))])]
""
[(set_attr "length" "2,8,10")
(set_attr "cc" "set_zn,set_zn,set_zn")])
 
(define_insn ""
[(set (cc0) (zero_extract:SI (match_operand:SI 0 "register_operand" "r")
(const_int 1)
(match_operand 1 "const_int_operand" "n")))]
"(TARGET_H8300H || TARGET_H8300S)
&& INTVAL (operands[1]) <= 15"
"btst %Z1,%Y0"
[(set_attr "length" "2")
(set_attr "cc" "set_zn")])
 
(define_insn_and_split "*tstsi_upper_bit"
[(set (cc0)
(zero_extract:SI (match_operand:SI 0 "register_operand" "r")
(const_int 1)
(match_operand 1 "const_int_operand" "n")))
(clobber (match_scratch:SI 2 "=&r"))]
"(TARGET_H8300H || TARGET_H8300S)
&& INTVAL (operands[1]) >= 16"
"#"
"&& reload_completed"
[(set (match_dup 2)
(ior:SI (and:SI (match_dup 2)
(const_int -65536))
(lshiftrt:SI (match_dup 0)
(const_int 16))))
(set (cc0)
(zero_extract:SI (match_dup 2)
(const_int 1)
(match_dup 3)))]
"operands[3] = GEN_INT (INTVAL (operands[1]) - 16);")
 
(define_insn "*tstsi_variable_bit"
[(set (cc0)
(zero_extract:SI (match_operand:SI 0 "register_operand" "r")
(const_int 1)
(and:SI (match_operand:SI 1 "register_operand" "r")
(const_int 7))))]
"TARGET_H8300H || TARGET_H8300S"
"btst %w1,%w0"
[(set_attr "length" "2")
(set_attr "cc" "set_zn")])
 
(define_insn_and_split "*tstsi_variable_bit_qi"
[(set (cc0)
(zero_extract:SI (zero_extend:SI (match_operand:QI 0 "general_operand_src" "r,U,mn>"))
(const_int 1)
(and:SI (match_operand:SI 1 "register_operand" "r,r,r")
(const_int 7))))
(clobber (match_scratch:QI 2 "=X,X,&r"))]
"(TARGET_H8300H || TARGET_H8300S)"
"@
btst\\t%w1,%X0
btst\\t%w1,%X0
#"
"&& reload_completed
&& !OK_FOR_U (operands[0])"
[(set (match_dup 2)
(match_dup 0))
(parallel [(set (cc0) (zero_extract:SI (zero_extend:SI (match_dup 2))
(const_int 1)
(and:SI (match_dup 1)
(const_int 7))))
(clobber (scratch:QI))])]
""
[(set_attr "length" "2,8,10")
(set_attr "cc" "set_zn,set_zn,set_zn")])
 
(define_insn "tstqi"
[(set (cc0) (match_operand:QI 0 "register_operand" "r"))]
""
"mov.b %X0,%X0"
[(set_attr "length" "2")
(set_attr "cc" "set_znv")])
 
(define_insn "tsthi"
[(set (cc0) (match_operand:HI 0 "register_operand" "r"))]
""
"mov.w %T0,%T0"
[(set_attr "length" "2")
(set_attr "cc" "set_znv")])
 
(define_insn "*tsthi_upper"
[(set (cc0)
(and:HI (match_operand:HI 0 "register_operand" "r")
(const_int -256)))]
""
"mov.b %t0,%t0"
[(set_attr "length" "2")
(set_attr "cc" "set_znv")])
 
(define_insn "tstsi"
[(set (cc0) (match_operand:SI 0 "register_operand" "r"))]
"TARGET_H8300H || TARGET_H8300S"
"mov.l %S0,%S0"
[(set_attr "length" "2")
(set_attr "cc" "set_znv")])
 
(define_insn "*tstsi_upper"
[(set (cc0)
(and:SI (match_operand:SI 0 "register_operand" "r")
(const_int -65536)))]
""
"mov.w %e0,%e0"
[(set_attr "length" "2")
(set_attr "cc" "set_znv")])
 
(define_insn "cmpqi"
[(set (cc0)
(compare (match_operand:QI 0 "h8300_dst_operand" "rQ")
(match_operand:QI 1 "h8300_src_operand" "rQi")))]
""
"cmp.b %X1,%X0"
[(set_attr "length_table" "addb")
(set_attr "cc" "compare")])
 
(define_expand "cmphi"
[(set (cc0)
(compare (match_operand:HI 0 "h8300_dst_operand" "")
(match_operand:HI 1 "h8300_src_operand" "")))]
""
"
{
/* Force operand1 into a register if we're compiling
for the H8/300. */
if (GET_CODE (operands[1]) != REG && TARGET_H8300)
operands[1] = force_reg (HImode, operands[1]);
}")
 
(define_insn "*cmphi_h8300_znvc"
[(set (cc0)
(compare (match_operand:HI 0 "register_operand" "r")
(match_operand:HI 1 "register_operand" "r")))]
"TARGET_H8300"
"cmp.w %T1,%T0"
[(set_attr "length" "2")
(set_attr "cc" "compare")])
 
(define_insn "*cmphi_h8300hs_znvc"
[(set (cc0)
(compare (match_operand:HI 0 "h8300_dst_operand" "rU,rQ")
(match_operand:HI 1 "h8300_src_operand" "P3>X,rQi")))]
"TARGET_H8300H || TARGET_H8300S"
"cmp.w %T1,%T0"
[(set_attr "length_table" "short_immediate,addw")
(set_attr "cc" "compare,compare")])
 
(define_insn "cmpsi"
[(set (cc0)
(compare (match_operand:SI 0 "h8300_dst_operand" "r,rQ")
(match_operand:SI 1 "h8300_src_operand" "P3>X,rQi")))]
"TARGET_H8300H || TARGET_H8300S"
"cmp.l %S1,%S0"
[(set_attr "length" "2,*")
(set_attr "length_table" "*,addl")
(set_attr "cc" "compare,compare")])
;; ----------------------------------------------------------------------
;; ADD INSTRUCTIONS
;; ----------------------------------------------------------------------
 
(define_expand "addqi3"
[(set (match_operand:QI 0 "register_operand" "")
(plus:QI (match_operand:QI 1 "register_operand" "")
(match_operand:QI 2 "h8300_src_operand" "")))]
""
"")
 
(define_insn "*addqi3"
[(set (match_operand:QI 0 "h8300_dst_operand" "=rQ")
(plus:QI (match_operand:QI 1 "h8300_dst_operand" "%0")
(match_operand:QI 2 "h8300_src_operand" "rQi")))]
"h8300_operands_match_p (operands)"
"add.b %X2,%X0"
[(set_attr "length_table" "addb")
(set_attr "cc" "set_zn")])
 
(define_expand "addhi3"
[(set (match_operand:HI 0 "register_operand" "")
(plus:HI (match_operand:HI 1 "register_operand" "")
(match_operand:HI 2 "h8300_src_operand" "")))]
""
"")
 
(define_insn "*addhi3_h8300"
[(set (match_operand:HI 0 "register_operand" "=r,r,r,r,r")
(plus:HI (match_operand:HI 1 "register_operand" "%0,0,0,0,0")
(match_operand:HI 2 "h8300_src_operand" "L,N,J,n,r")))]
"TARGET_H8300"
"@
adds %2,%T0
subs %G2,%T0
add.b %t2,%t0
add.b %s2,%s0\;addx %t2,%t0
add.w %T2,%T0"
[(set_attr "length" "2,2,2,4,2")
(set_attr "cc" "none_0hit,none_0hit,clobber,clobber,set_zn")])
 
;; This splitter is very important to make the stack adjustment
;; interrupt-safe. The combination of add.b and addx is unsafe!
;;
;; We apply this split after the peephole2 pass so that we won't end
;; up creating too many adds/subs when a scratch register is
;; available, which is actually a common case because stack unrolling
;; tends to happen immediately after a function call.
 
(define_split
[(set (match_operand:HI 0 "stack_pointer_operand" "")
(plus:HI (match_dup 0)
(match_operand 1 "const_int_gt_2_operand" "")))]
"TARGET_H8300 && flow2_completed"
[(const_int 0)]
"split_adds_subs (HImode, operands); DONE;")
 
(define_peephole2
[(match_scratch:HI 2 "r")
(set (match_operand:HI 0 "stack_pointer_operand" "")
(plus:HI (match_dup 0)
(match_operand:HI 1 "const_int_ge_8_operand" "")))]
"TARGET_H8300"
[(set (match_dup 2)
(match_dup 1))
(set (match_dup 0)
(plus:HI (match_dup 0)
(match_dup 2)))]
"")
 
(define_insn "*addhi3_h8300hs"
[(set (match_operand:HI 0 "register_operand" "=r,r,r,r,r")
(plus:HI (match_operand:HI 1 "register_operand" "%0,0,0,0,0")
(match_operand:HI 2 "h8300_src_operand" "L,N,J,n,r")))]
"(TARGET_H8300H || TARGET_H8300S) && !TARGET_H8300SX"
"@
adds %2,%S0
subs %G2,%S0
add.b %t2,%t0
add.w %T2,%T0
add.w %T2,%T0"
[(set_attr "length" "2,2,2,4,2")
(set_attr "cc" "none_0hit,none_0hit,clobber,set_zn,set_zn")])
 
(define_insn "*addhi3_incdec"
[(set (match_operand:HI 0 "register_operand" "=r,r")
(unspec:HI [(match_operand:HI 1 "register_operand" "0,0")
(match_operand:HI 2 "incdec_operand" "M,O")]
UNSPEC_INCDEC))]
"TARGET_H8300H || TARGET_H8300S"
"@
inc.w %2,%T0
dec.w %G2,%T0"
[(set_attr "length" "2,2")
(set_attr "cc" "set_zn,set_zn")])
 
(define_insn "*addhi3_h8sx"
[(set (match_operand:HI 0 "h8300_dst_operand" "=rU,rU,r,rQ")
(plus:HI (match_operand:HI 1 "h8300_dst_operand" "%0,0,0,0")
(match_operand:HI 2 "h8300_src_operand" "P3>X,P3<X,J,rQi")))]
"TARGET_H8300SX && h8300_operands_match_p (operands)"
"@
add.w %T2,%T0
sub.w %G2,%T0
add.b %t2,%t0
add.w %T2,%T0"
[(set_attr "length_table" "short_immediate,short_immediate,*,addw")
(set_attr "length" "*,*,2,*")
(set_attr "cc" "set_zn")])
 
(define_split
[(set (match_operand:HI 0 "register_operand" "")
(plus:HI (match_dup 0)
(match_operand:HI 1 "two_insn_adds_subs_operand" "")))]
""
[(const_int 0)]
"split_adds_subs (HImode, operands); DONE;")
 
(define_expand "addsi3"
[(set (match_operand:SI 0 "register_operand" "")
(plus:SI (match_operand:SI 1 "register_operand" "")
(match_operand:SI 2 "h8300_src_operand" "")))]
""
"")
 
(define_insn "*addsi_h8300"
[(set (match_operand:SI 0 "register_operand" "=r,r")
(plus:SI (match_operand:SI 1 "register_operand" "%0,0")
(match_operand:SI 2 "h8300_src_operand" "n,r")))]
"TARGET_H8300"
"* return output_plussi (operands);"
[(set (attr "length")
(symbol_ref "compute_plussi_length (operands)"))
(set (attr "cc")
(symbol_ref "compute_plussi_cc (operands)"))])
 
(define_insn "*addsi_h8300hs"
[(set (match_operand:SI 0 "h8300_dst_operand" "=rQ,rQ")
(plus:SI (match_operand:SI 1 "h8300_dst_operand" "%0,0")
(match_operand:SI 2 "h8300_src_operand" "i,rQ")))]
"(TARGET_H8300H || TARGET_H8300S) && h8300_operands_match_p (operands)"
"* return output_plussi (operands);"
[(set (attr "length")
(symbol_ref "compute_plussi_length (operands)"))
(set (attr "cc")
(symbol_ref "compute_plussi_cc (operands)"))])
 
(define_insn "*addsi3_incdec"
[(set (match_operand:SI 0 "register_operand" "=r,r")
(unspec:SI [(match_operand:SI 1 "register_operand" "0,0")
(match_operand:SI 2 "incdec_operand" "M,O")]
UNSPEC_INCDEC))]
"TARGET_H8300H || TARGET_H8300S"
"@
inc.l %2,%S0
dec.l %G2,%S0"
[(set_attr "length" "2,2")
(set_attr "cc" "set_zn,set_zn")])
 
(define_split
[(set (match_operand:SI 0 "register_operand" "")
(plus:SI (match_dup 0)
(match_operand:SI 1 "two_insn_adds_subs_operand" "")))]
"TARGET_H8300H || TARGET_H8300S"
[(const_int 0)]
"split_adds_subs (SImode, operands); DONE;")
 
;; ----------------------------------------------------------------------
;; SUBTRACT INSTRUCTIONS
;; ----------------------------------------------------------------------
 
(define_expand "subqi3"
[(set (match_operand:QI 0 "register_operand" "")
(minus:QI (match_operand:QI 1 "register_operand" "")
(match_operand:QI 2 "h8300_src_operand" "")))]
""
"")
 
(define_insn "*subqi3"
[(set (match_operand:QI 0 "h8300_dst_operand" "=rQ")
(minus:QI (match_operand:QI 1 "h8300_dst_operand" "0")
(match_operand:QI 2 "h8300_dst_operand" "rQ")))]
"h8300_operands_match_p (operands)"
"sub.b %X2,%X0"
[(set_attr "length_table" "addb")
(set_attr "cc" "set_zn")])
 
(define_expand "subhi3"
[(set (match_operand:HI 0 "register_operand" "")
(minus:HI (match_operand:HI 1 "register_operand" "")
(match_operand:HI 2 "h8300_src_operand" "")))]
""
"")
 
(define_insn "*subhi3_h8300"
[(set (match_operand:HI 0 "register_operand" "=r,r")
(minus:HI (match_operand:HI 1 "register_operand" "0,0")
(match_operand:HI 2 "h8300_src_operand" "r,n")))]
"TARGET_H8300"
"@
sub.w %T2,%T0
add.b %E2,%s0\;addx %F2,%t0"
[(set_attr "length" "2,4")
(set_attr "cc" "set_zn,clobber")])
 
(define_insn "*subhi3_h8300hs"
[(set (match_operand:HI 0 "h8300_dst_operand" "=rQ,rQ")
(minus:HI (match_operand:HI 1 "h8300_dst_operand" "0,0")
(match_operand:HI 2 "h8300_src_operand" "rQ,i")))]
"(TARGET_H8300H || TARGET_H8300S) && h8300_operands_match_p (operands)"
"@
sub.w %T2,%T0
sub.w %T2,%T0"
[(set_attr "length_table" "addw")
(set_attr "cc" "set_zn")])
 
(define_expand "subsi3"
[(set (match_operand:SI 0 "register_operand" "")
(minus:SI (match_operand:SI 1 "register_operand" "")
(match_operand:SI 2 "h8300_src_operand" "")))]
""
{
if (TARGET_H8300)
operands[2] = force_reg (SImode, operands[2]);
})
 
(define_insn "*subsi3_h8300"
[(set (match_operand:SI 0 "register_operand" "=r")
(minus:SI (match_operand:SI 1 "register_operand" "0")
(match_operand:SI 2 "register_operand" "r")))]
"TARGET_H8300"
"sub.w %f2,%f0\;subx %y2,%y0\;subx %z2,%z0"
[(set_attr "length" "6")])
 
(define_insn "*subsi3_h8300hs"
[(set (match_operand:SI 0 "h8300_dst_operand" "=rQ,rQ")
(minus:SI (match_operand:SI 1 "h8300_dst_operand" "0,0")
(match_operand:SI 2 "h8300_src_operand" "rQ,i")))]
"(TARGET_H8300H || TARGET_H8300S) && h8300_operands_match_p (operands)"
"@
sub.l %S2,%S0
sub.l %S2,%S0"
[(set_attr "length_table" "addl")
(set_attr "cc" "set_zn")])
;; ----------------------------------------------------------------------
;; MULTIPLY INSTRUCTIONS
;; ----------------------------------------------------------------------
 
;; Note that the H8/300 can only handle umulqihi3.
 
(define_expand "mulqihi3"
[(set (match_operand:HI 0 "register_operand" "")
(mult:HI (sign_extend:HI (match_operand:QI 1 "register_operand" ""))
;; intentionally-mismatched modes
(match_operand:QI 2 "reg_or_nibble_operand" "")))]
"TARGET_H8300H || TARGET_H8300S"
"
{
if (GET_MODE (operands[2]) != VOIDmode)
operands[2] = gen_rtx_SIGN_EXTEND (HImode, operands[2]);
}")
 
(define_insn "*mulqihi3_const"
[(set (match_operand:HI 0 "register_operand" "=r")
(mult:HI (sign_extend:HI (match_operand:QI 1 "register_operand" "%0"))
(match_operand:QI 2 "nibble_operand" "IP4>X")))]
"TARGET_H8300SX"
"mulxs.b %X2,%T0"
[(set_attr "length" "4")
(set_attr "cc" "set_zn")])
 
(define_insn "*mulqihi3"
[(set (match_operand:HI 0 "register_operand" "=r")
(mult:HI (sign_extend:HI (match_operand:QI 1 "register_operand" "%0"))
(sign_extend:HI (match_operand:QI 2 "register_operand" "r"))))]
"TARGET_H8300H || TARGET_H8300S"
"mulxs.b %X2,%T0"
[(set_attr "length" "4")
(set_attr "cc" "set_zn")])
 
(define_expand "mulhisi3"
[(set (match_operand:SI 0 "register_operand" "")
(mult:SI (sign_extend:SI (match_operand:HI 1 "register_operand" ""))
;; intentionally-mismatched modes
(match_operand:HI 2 "reg_or_nibble_operand" "")))]
"TARGET_H8300H || TARGET_H8300S"
"
{
if (GET_MODE (operands[2]) != VOIDmode)
operands[2] = gen_rtx_SIGN_EXTEND (SImode, operands[2]);
}")
 
(define_insn "*mulhisi3_const"
[(set (match_operand:SI 0 "register_operand" "=r")
(mult:SI (sign_extend:SI (match_operand:HI 1 "register_operand" "%0"))
(match_operand:SI 2 "nibble_operand" "IP4>X")))]
"TARGET_H8300SX"
"mulxs.w %T2,%S0"
[(set_attr "length" "4")
(set_attr "cc" "set_zn")])
 
(define_insn "*mulhisi3"
[(set (match_operand:SI 0 "register_operand" "=r")
(mult:SI (sign_extend:SI (match_operand:HI 1 "register_operand" "%0"))
(sign_extend:SI (match_operand:HI 2 "register_operand" "r"))))]
"TARGET_H8300H || TARGET_H8300S"
"mulxs.w %T2,%S0"
[(set_attr "length" "4")
(set_attr "cc" "set_zn")])
 
(define_expand "umulqihi3"
[(set (match_operand:HI 0 "register_operand" "")
(mult:HI (zero_extend:HI (match_operand:QI 1 "register_operand" ""))
;; intentionally-mismatched modes
(match_operand:QI 2 "reg_or_nibble_operand" "")))]
"TARGET_H8300H || TARGET_H8300S"
"
{
if (GET_MODE (operands[2]) != VOIDmode)
operands[2] = gen_rtx_ZERO_EXTEND (HImode, operands[2]);
}")
 
(define_insn "*umulqihi3_const"
[(set (match_operand:HI 0 "register_operand" "=r")
(mult:HI (zero_extend:HI (match_operand:QI 1 "register_operand" "%0"))
(match_operand:QI 2 "nibble_operand" "IP4>X")))]
"TARGET_H8300SX"
"mulxu.b %X2,%T0"
[(set_attr "length" "4")
(set_attr "cc" "set_zn")])
 
(define_insn "*umulqihi3"
[(set (match_operand:HI 0 "register_operand" "=r")
(mult:HI (zero_extend:HI (match_operand:QI 1 "register_operand" "%0"))
(zero_extend:HI (match_operand:QI 2 "register_operand" "r"))))]
""
"mulxu.b %X2,%T0"
[(set_attr "length" "2")
(set_attr "cc" "none_0hit")])
 
(define_expand "umulhisi3"
[(set (match_operand:SI 0 "register_operand" "")
(mult:SI (zero_extend:SI (match_operand:HI 1 "register_operand" ""))
;; intentionally-mismatched modes
(match_operand:HI 2 "reg_or_nibble_operand" "")))]
"TARGET_H8300H || TARGET_H8300S"
"
{
if (GET_MODE (operands[2]) != VOIDmode)
operands[2] = gen_rtx_ZERO_EXTEND (SImode, operands[2]);
}")
 
(define_insn "*umulhisi3_const"
[(set (match_operand:SI 0 "register_operand" "=r")
(mult:SI (zero_extend:SI (match_operand:HI 1 "register_operand" "%0"))
(match_operand:SI 2 "nibble_operand" "IP4>X")))]
"TARGET_H8300SX"
"mulxu.w %T2,%S0"
[(set_attr "length" "4")
(set_attr "cc" "set_zn")])
 
(define_insn "*umulhisi3"
[(set (match_operand:SI 0 "register_operand" "=r")
(mult:SI (zero_extend:SI (match_operand:HI 1 "register_operand" "%0"))
(zero_extend:SI (match_operand:HI 2 "register_operand" "r"))))]
"TARGET_H8300H || TARGET_H8300S"
"mulxu.w %T2,%S0"
[(set_attr "length" "2")
(set_attr "cc" "none_0hit")])
 
;; We could have used mulu.[wl] here, but mulu.[lw] is only available
;; on a H8SX with a multiplier, whereas muls.w seems to be available
;; on all H8SX variants.
(define_insn "mulhi3"
[(set (match_operand:HI 0 "register_operand" "=r")
(mult:HI (match_operand:HI 1 "register_operand" "%0")
(match_operand:HI 2 "reg_or_nibble_operand" "r IP4>X")))]
"TARGET_H8300SX"
"muls.w\\t%T2,%T0"
[(set_attr "length" "2")
(set_attr "cc" "set_zn")])
 
(define_insn "mulsi3"
[(set (match_operand:SI 0 "register_operand" "=r")
(mult:SI (match_operand:SI 1 "register_operand" "%0")
(match_operand:SI 2 "reg_or_nibble_operand" "r IP4>X")))]
"TARGET_H8300SX"
"muls.l\\t%S2,%S0"
[(set_attr "length" "2")
(set_attr "cc" "set_zn")])
 
(define_insn "smulsi3_highpart"
[(set (match_operand:SI 0 "register_operand" "=r")
(truncate:SI
(lshiftrt:DI
(mult:DI
(sign_extend:DI (match_operand:SI 1 "register_operand" "%0"))
(sign_extend:DI (match_operand:SI 2 "reg_or_nibble_operand" "r IP4>X")))
(const_int 32))))]
"TARGET_H8300SXMUL"
"muls/u.l\\t%S2,%S0"
[(set_attr "length" "2")
(set_attr "cc" "set_zn")])
 
(define_insn "umulsi3_highpart"
[(set (match_operand:SI 0 "register_operand" "=r")
(truncate:SI
(ashiftrt:DI
(mult:DI
(zero_extend:DI (match_operand:SI 1 "register_operand" "%0"))
(zero_extend:DI (match_operand:SI 2 "reg_or_nibble_operand" "r IP4>X")))
(const_int 32))))]
"TARGET_H8300SX"
"mulu/u.l\\t%S2,%S0"
[(set_attr "length" "2")
(set_attr "cc" "none_0hit")])
 
;; This is a "bridge" instruction. Combine can't cram enough insns
;; together to crate a MAC instruction directly, but it can create
;; this instruction, which then allows combine to create the real
;; MAC insn.
;;
;; Unfortunately, if combine doesn't create a MAC instruction, this
;; insn must generate reasonably correct code. Egad.
(define_insn ""
[(set (match_operand:SI 0 "register_operand" "=a")
(mult:SI
(sign_extend:SI
(mem:HI (post_inc:SI (match_operand:SI 1 "register_operand" "r"))))
(sign_extend:SI
(mem:HI (post_inc:SI (match_operand:SI 2 "register_operand" "r"))))))]
"TARGET_MAC"
"clrmac\;mac @%2+,@%1+"
[(set_attr "length" "6")
(set_attr "cc" "none_0hit")])
 
(define_insn ""
[(set (match_operand:SI 0 "register_operand" "=a")
(plus:SI (mult:SI
(sign_extend:SI (mem:HI
(post_inc:SI (match_operand:SI 1 "register_operand" "r"))))
(sign_extend:SI (mem:HI
(post_inc:SI (match_operand:SI 2 "register_operand" "r")))))
(match_operand:SI 3 "register_operand" "0")))]
"TARGET_MAC"
"mac @%2+,@%1+"
[(set_attr "length" "4")
(set_attr "cc" "none_0hit")])
 
;; ----------------------------------------------------------------------
;; DIVIDE/MOD INSTRUCTIONS
;; ----------------------------------------------------------------------
 
(define_insn "udivhi3"
[(set (match_operand:HI 0 "register_operand" "=r")
(udiv:HI
(match_operand:HI 1 "register_operand" "0")
(match_operand:HI 2 "reg_or_nibble_operand" "r IP4>X")))]
"TARGET_H8300SX"
"divu.w\\t%T2,%T0"
[(set_attr "length" "2")])
(define_insn "divhi3"
[(set (match_operand:HI 0 "register_operand" "=r")
(div:HI
(match_operand:HI 1 "register_operand" "0")
(match_operand:HI 2 "reg_or_nibble_operand" "r IP4>X")))]
"TARGET_H8300SX"
"divs.w\\t%T2,%T0"
[(set_attr "length" "2")])
(define_insn "udivsi3"
[(set (match_operand:SI 0 "register_operand" "=r")
(udiv:SI
(match_operand:SI 1 "register_operand" "0")
(match_operand:SI 2 "reg_or_nibble_operand" "r IP4>X")))]
"TARGET_H8300SX"
"divu.l\\t%S2,%S0"
[(set_attr "length" "2")])
(define_insn "divsi3"
[(set (match_operand:SI 0 "register_operand" "=r")
(div:SI
(match_operand:SI 1 "register_operand" "0")
(match_operand:SI 2 "reg_or_nibble_operand" "r IP4>X")))]
"TARGET_H8300SX"
"divs.l\\t%S2,%S0"
[(set_attr "length" "2")])
(define_insn "udivmodqi4"
[(set (match_operand:QI 0 "register_operand" "=r")
(truncate:QI
(udiv:HI
(match_operand:HI 1 "register_operand" "0")
(zero_extend:HI (match_operand:QI 2 "register_operand" "r")))))
(set (match_operand:QI 3 "register_operand" "=r")
(truncate:QI
(umod:HI
(match_dup 1)
(zero_extend:HI (match_dup 2)))))]
""
"*
{
if (find_reg_note (insn, REG_UNUSED, operands[3]))
return \"divxu.b\\t%X2,%T0\";
else
return \"divxu.b\\t%X2,%T0\;mov.b\\t%t0,%s3\";
}"
[(set_attr "length" "4")])
 
(define_insn "divmodqi4"
[(set (match_operand:QI 0 "register_operand" "=r")
(truncate:QI
(div:HI
(match_operand:HI 1 "register_operand" "0")
(sign_extend:HI (match_operand:QI 2 "register_operand" "r")))))
(set (match_operand:QI 3 "register_operand" "=r")
(truncate:QI
(mod:HI
(match_dup 1)
(sign_extend:HI (match_dup 2)))))]
"TARGET_H8300H || TARGET_H8300S"
"*
{
if (find_reg_note (insn, REG_UNUSED, operands[3]))
return \"divxs.b\\t%X2,%T0\";
else
return \"divxs.b\\t%X2,%T0\;mov.b\\t%t0,%s3\";
}"
[(set_attr "length" "6")])
 
(define_insn "udivmodhi4"
[(set (match_operand:HI 0 "register_operand" "=r")
(truncate:HI
(udiv:SI
(match_operand:SI 1 "register_operand" "0")
(zero_extend:SI (match_operand:HI 2 "register_operand" "r")))))
(set (match_operand:HI 3 "register_operand" "=r")
(truncate:HI
(umod:SI
(match_dup 1)
(zero_extend:SI (match_dup 2)))))]
"TARGET_H8300H || TARGET_H8300S"
"*
{
if (find_reg_note (insn, REG_UNUSED, operands[3]))
return \"divxu.w\\t%T2,%S0\";
else
return \"divxu.w\\t%T2,%S0\;mov.w\\t%e0,%f3\";
}"
[(set_attr "length" "4")])
 
(define_insn "divmodhi4"
[(set (match_operand:HI 0 "register_operand" "=r")
(truncate:HI
(div:SI
(match_operand:SI 1 "register_operand" "0")
(sign_extend:SI (match_operand:HI 2 "register_operand" "r")))))
(set (match_operand:HI 3 "register_operand" "=r")
(truncate:HI
(mod:SI
(match_dup 1)
(sign_extend:SI (match_dup 2)))))]
"TARGET_H8300H || TARGET_H8300S"
"*
{
if (find_reg_note (insn, REG_UNUSED, operands[3]))
return \"divxs.w\\t%T2,%S0\";
else
return \"divxs.w\\t%T2,%S0\;mov.w\\t%e0,%f3\";
}"
[(set_attr "length" "6")])
;; ----------------------------------------------------------------------
;; AND INSTRUCTIONS
;; ----------------------------------------------------------------------
 
(define_insn "*andqi3_2"
[(set (match_operand:QI 0 "bit_operand" "=rQ,r")
(and:QI (match_operand:QI 1 "bit_operand" "%0,WU")
(match_operand:QI 2 "h8300_src_operand" "rQi,IP1>X")))]
"TARGET_H8300SX"
"@
and %X2,%X0
bfld %2,%1,%R0"
[(set_attr "length" "*,8")
(set_attr "length_table" "logicb,*")
(set_attr "cc" "set_znv,none_0hit")])
 
(define_insn "andqi3_1"
[(set (match_operand:QI 0 "bit_operand" "=r,U")
(and:QI (match_operand:QI 1 "bit_operand" "%0,0")
(match_operand:QI 2 "h8300_src_operand" "rn,n")))]
"register_operand (operands[0], QImode)
|| single_zero_operand (operands[2], QImode)"
"@
and %X2,%X0
bclr %W2,%R0"
[(set_attr "length" "2,8")
(set_attr "cc" "set_znv,none_0hit")])
 
(define_expand "andqi3"
[(set (match_operand:QI 0 "register_operand" "")
(and:QI (match_operand:QI 1 "register_operand" "")
(match_operand:QI 2 "h8300_src_operand" "")))]
""
"")
 
(define_expand "andhi3"
[(set (match_operand:HI 0 "register_operand" "")
(and:HI (match_operand:HI 1 "register_operand" "")
(match_operand:HI 2 "h8300_src_operand" "")))]
""
"")
 
(define_insn "*andorqi3"
[(set (match_operand:QI 0 "register_operand" "=r")
(ior:QI (and:QI (match_operand:QI 2 "register_operand" "r")
(match_operand:QI 3 "single_one_operand" "n"))
(match_operand:QI 1 "register_operand" "0")))]
""
"bld\\t%V3,%X2\;bor\\t%V3,%X0\;bst\\t%V3,%X0"
[(set_attr "length" "6")])
 
(define_insn "*andorhi3"
[(set (match_operand:HI 0 "register_operand" "=r")
(ior:HI (and:HI (match_operand:HI 2 "register_operand" "r")
(match_operand:HI 3 "single_one_operand" "n"))
(match_operand:HI 1 "register_operand" "0")))]
""
"*
{
operands[3] = GEN_INT (INTVAL (operands[3]) & 0xffff);
if (INTVAL (operands[3]) > 128)
{
operands[3] = GEN_INT (INTVAL (operands[3]) >> 8);
return \"bld\\t%V3,%t2\;bor\\t%V3,%t0\;bst\\t%V3,%t0\";
}
return \"bld\\t%V3,%s2\;bor\\t%V3,%s0\;bst\\t%V3,%s0\";
}"
[(set_attr "length" "6")])
 
(define_insn "*andorsi3"
[(set (match_operand:SI 0 "register_operand" "=r")
(ior:SI (and:SI (match_operand:SI 2 "register_operand" "r")
(match_operand:SI 3 "single_one_operand" "n"))
(match_operand:SI 1 "register_operand" "0")))]
"(INTVAL (operands[3]) & 0xffff) != 0"
"*
{
operands[3] = GEN_INT (INTVAL (operands[3]) & 0xffff);
if (INTVAL (operands[3]) > 128)
{
operands[3] = GEN_INT (INTVAL (operands[3]) >> 8);
return \"bld\\t%V3,%x2\;bor\\t%V3,%x0\;bst\\t%V3,%x0\";
}
return \"bld\\t%V3,%w2\;bor\\t%V3,%w0\;bst\\t%V3,%w0\";
}"
[(set_attr "length" "6")])
 
(define_insn "*andorsi3_shift_8"
[(set (match_operand:SI 0 "register_operand" "=r")
(ior:SI (and:SI (ashift:SI (match_operand:SI 2 "register_operand" "r")
(const_int 8))
(const_int 65280))
(match_operand:SI 1 "register_operand" "0")))]
""
"or.b\\t%w2,%x0"
[(set_attr "length" "2")])
 
(define_expand "andsi3"
[(set (match_operand:SI 0 "register_operand" "")
(and:SI (match_operand:SI 1 "register_operand" "")
(match_operand:SI 2 "h8300_src_operand" "")))]
""
"")
 
;; ----------------------------------------------------------------------
;; OR INSTRUCTIONS
;; ----------------------------------------------------------------------
 
(define_insn "iorqi3_1"
[(set (match_operand:QI 0 "bit_operand" "=rQ,U")
(ior:QI (match_operand:QI 1 "bit_operand" "%0,0")
(match_operand:QI 2 "h8300_src_operand" "rQi,n")))]
"TARGET_H8300SX || register_operand (operands[0], QImode)
|| single_one_operand (operands[2], QImode)"
"@
or\\t%X2,%X0
bset\\t%V2,%R0"
[(set_attr "length" "*,8")
(set_attr "length_table" "logicb,*")
(set_attr "cc" "set_znv,none_0hit")])
 
(define_expand "iorqi3"
[(set (match_operand:QI 0 "register_operand" "")
(ior:QI (match_operand:QI 1 "register_operand" "")
(match_operand:QI 2 "h8300_src_operand" "")))]
""
"")
 
(define_expand "iorhi3"
[(set (match_operand:HI 0 "register_operand" "")
(ior:HI (match_operand:HI 1 "register_operand" "")
(match_operand:HI 2 "h8300_src_operand" "")))]
""
"")
 
(define_expand "iorsi3"
[(set (match_operand:SI 0 "register_operand" "")
(ior:SI (match_operand:SI 1 "register_operand" "")
(match_operand:SI 2 "h8300_src_operand" "")))]
""
"")
 
;; ----------------------------------------------------------------------
;; XOR INSTRUCTIONS
;; ----------------------------------------------------------------------
 
(define_insn "xorqi3_1"
[(set (match_operand:QI 0 "bit_operand" "=r,U")
(xor:QI (match_operand:QI 1 "bit_operand" "%0,0")
(match_operand:QI 2 "h8300_src_operand" "rQi,n")))]
"TARGET_H8300SX || register_operand (operands[0], QImode)
|| single_one_operand (operands[2], QImode)"
"@
xor\\t%X2,%X0
bnot\\t%V2,%R0"
[(set_attr "length" "*,8")
(set_attr "length_table" "logicb,*")
(set_attr "cc" "set_znv,none_0hit")])
 
(define_expand "xorqi3"
[(set (match_operand:QI 0 "register_operand" "")
(xor:QI (match_operand:QI 1 "register_operand" "")
(match_operand:QI 2 "h8300_src_operand" "")))]
""
"")
 
(define_expand "xorhi3"
[(set (match_operand:HI 0 "register_operand" "")
(xor:HI (match_operand:HI 1 "register_operand" "")
(match_operand:HI 2 "h8300_src_operand" "")))]
""
"")
 
(define_expand "xorsi3"
[(set (match_operand:SI 0 "register_operand" "")
(xor:SI (match_operand:SI 1 "register_operand" "")
(match_operand:SI 2 "h8300_src_operand" "")))]
""
"")
 
;; ----------------------------------------------------------------------
;; {AND,IOR,XOR}{HI3,SI3} PATTERNS
;; ----------------------------------------------------------------------
 
;; We need a separate pattern here because machines other than the
;; original H8300 don't have to split the 16-bit operand into a pair
;; of high/low instructions, so we can accept literal addresses, that
;; have to be loaded into a register on H8300.
(define_insn "*logicalhi3_sn"
[(set (match_operand:HI 0 "h8300_dst_operand" "=rQ")
(match_operator:HI 3 "bit_operator"
[(match_operand:HI 1 "h8300_dst_operand" "%0")
(match_operand:HI 2 "h8300_src_operand" "rQi")]))]
"(TARGET_H8300S || TARGET_H8300H) && h8300_operands_match_p (operands)"
"* return output_logical_op (HImode, operands);"
[(set (attr "length")
(symbol_ref "compute_logical_op_length (HImode, operands)"))
(set (attr "cc")
(symbol_ref "compute_logical_op_cc (HImode, operands)"))])
 
(define_insn "*logicalsi3_sn"
[(set (match_operand:SI 0 "h8300_dst_operand" "=rQ")
(match_operator:SI 3 "bit_operator"
[(match_operand:SI 1 "h8300_dst_operand" "%0")
(match_operand:SI 2 "h8300_src_operand" "rQi")]))]
"(TARGET_H8300S || TARGET_H8300H) && h8300_operands_match_p (operands)"
"* return output_logical_op (SImode, operands);"
[(set (attr "length")
(symbol_ref "compute_logical_op_length (SImode, operands)"))
(set (attr "cc")
(symbol_ref "compute_logical_op_cc (SImode, operands)"))])
 
(define_insn "*logicalhi3"
[(set (match_operand:HI 0 "h8300_dst_operand" "=rQ")
(match_operator:HI 3 "bit_operator"
[(match_operand:HI 1 "h8300_dst_operand" "%0")
(match_operand:HI 2 "h8300_src_operand" "rQi")]))]
"h8300_operands_match_p (operands)"
"* return output_logical_op (HImode, operands);"
[(set (attr "length")
(symbol_ref "compute_logical_op_length (HImode, operands)"))
(set (attr "cc")
(symbol_ref "compute_logical_op_cc (HImode, operands)"))])
 
(define_insn "*logicalsi3"
[(set (match_operand:SI 0 "h8300_dst_operand" "=rQ")
(match_operator:SI 3 "bit_operator"
[(match_operand:SI 1 "h8300_dst_operand" "%0")
(match_operand:SI 2 "h8300_src_operand" "rQi")]))]
"h8300_operands_match_p (operands)"
"* return output_logical_op (SImode, operands);"
[(set (attr "length")
(symbol_ref "compute_logical_op_length (SImode, operands)"))
(set (attr "cc")
(symbol_ref "compute_logical_op_cc (SImode, operands)"))])
;; ----------------------------------------------------------------------
;; NEGATION INSTRUCTIONS
;; ----------------------------------------------------------------------
 
(define_expand "negqi2"
[(set (match_operand:QI 0 "register_operand" "")
(neg:QI (match_operand:QI 1 "register_operand" "")))]
""
"")
 
(define_insn "*negqi2"
[(set (match_operand:QI 0 "h8300_dst_operand" "=rQ")
(neg:QI (match_operand:QI 1 "h8300_dst_operand" "0")))]
""
"neg %X0"
[(set_attr "length_table" "unary")
(set_attr "cc" "set_zn")])
 
(define_expand "neghi2"
[(set (match_operand:HI 0 "register_operand" "")
(neg:HI (match_operand:HI 1 "register_operand" "")))]
""
"
{
if (TARGET_H8300)
{
emit_insn (gen_neghi2_h8300 (operands[0], operands[1]));
DONE;
}
}")
 
(define_expand "neghi2_h8300"
[(set (match_dup 2)
(not:HI (match_operand:HI 1 "register_operand" "")))
(set (match_dup 2) (plus:HI (match_dup 2) (const_int 1)))
(set (match_operand:HI 0 "register_operand" "")
(match_dup 2))]
""
"operands[2] = gen_reg_rtx (HImode);")
 
(define_insn "*neghi2_h8300hs"
[(set (match_operand:HI 0 "h8300_dst_operand" "=rQ")
(neg:HI (match_operand:HI 1 "h8300_dst_operand" "0")))]
"(TARGET_H8300H || TARGET_H8300S) && h8300_operands_match_p (operands)"
"neg.w %T0"
[(set_attr "length_table" "unary")
(set_attr "cc" "set_zn")])
 
(define_expand "negsi2"
[(set (match_operand:SI 0 "register_operand" "")
(neg:SI (match_operand:SI 1 "register_operand" "")))]
""
"
{
if (TARGET_H8300)
{
emit_insn (gen_negsi2_h8300 (operands[0], operands[1]));
DONE;
}
}")
 
(define_expand "negsi2_h8300"
[(set (match_dup 2)
(not:SI (match_operand:SI 1 "register_operand" "")))
(set (match_dup 2) (plus:SI (match_dup 2) (const_int 1)))
(set (match_operand:SI 0 "register_operand" "")
(match_dup 2))]
""
"operands[2] = gen_reg_rtx (SImode);")
 
(define_insn "*negsi2_h8300hs"
[(set (match_operand:SI 0 "h8300_dst_operand" "=rQ")
(neg:SI (match_operand:SI 1 "h8300_dst_operand" "0")))]
"(TARGET_H8300H || TARGET_H8300S) && h8300_operands_match_p (operands)"
"neg.l %S0"
[(set_attr "length_table" "unary")
(set_attr "cc" "set_zn")])
 
(define_expand "negsf2"
[(set (match_operand:SF 0 "register_operand" "")
(neg:SF (match_operand:SF 1 "register_operand" "")))]
""
"")
 
(define_insn "*negsf2_h8300"
[(set (match_operand:SF 0 "register_operand" "=r")
(neg:SF (match_operand:SF 1 "register_operand" "0")))]
"TARGET_H8300"
"xor.b\\t#128,%z0"
[(set_attr "length" "2")])
 
(define_insn "*negsf2_h8300hs"
[(set (match_operand:SF 0 "register_operand" "=r")
(neg:SF (match_operand:SF 1 "register_operand" "0")))]
"TARGET_H8300H || TARGET_H8300S"
"xor.w\\t#32768,%e0"
[(set_attr "length" "4")])
;; ----------------------------------------------------------------------
;; ABSOLUTE VALUE INSTRUCTIONS
;; ----------------------------------------------------------------------
 
(define_expand "abssf2"
[(set (match_operand:SF 0 "register_operand" "")
(abs:SF (match_operand:SF 1 "register_operand" "")))]
""
"")
 
(define_insn "*abssf2_h8300"
[(set (match_operand:SF 0 "register_operand" "=r")
(abs:SF (match_operand:SF 1 "register_operand" "0")))]
"TARGET_H8300"
"and.b\\t#127,%z0"
[(set_attr "length" "2")])
 
(define_insn "*abssf2_h8300hs"
[(set (match_operand:SF 0 "register_operand" "=r")
(abs:SF (match_operand:SF 1 "register_operand" "0")))]
"TARGET_H8300H || TARGET_H8300S"
"and.w\\t#32767,%e0"
[(set_attr "length" "4")])
;; ----------------------------------------------------------------------
;; NOT INSTRUCTIONS
;; ----------------------------------------------------------------------
 
(define_expand "one_cmplqi2"
[(set (match_operand:QI 0 "register_operand" "")
(not:QI (match_operand:QI 1 "register_operand" "")))]
""
"")
 
(define_insn "*one_cmplqi2"
[(set (match_operand:QI 0 "h8300_dst_operand" "=rQ")
(not:QI (match_operand:QI 1 "h8300_dst_operand" "0")))]
""
"not %X0"
[(set_attr "length_table" "unary")
(set_attr "cc" "set_znv")])
 
(define_expand "one_cmplhi2"
[(set (match_operand:HI 0 "register_operand" "")
(not:HI (match_operand:HI 1 "register_operand" "")))]
""
"")
 
(define_insn "*one_cmplhi2_h8300"
[(set (match_operand:HI 0 "register_operand" "=r")
(not:HI (match_operand:HI 1 "register_operand" "0")))]
"TARGET_H8300"
"not %s0\;not %t0"
[(set_attr "length" "4")])
 
(define_insn "*one_cmplhi2_h8300hs"
[(set (match_operand:HI 0 "h8300_dst_operand" "=rQ")
(not:HI (match_operand:HI 1 "h8300_dst_operand" "0")))]
"(TARGET_H8300H || TARGET_H8300S) && h8300_operands_match_p (operands)"
"not.w %T0"
[(set_attr "cc" "set_znv")
(set_attr "length_table" "unary")])
 
(define_expand "one_cmplsi2"
[(set (match_operand:SI 0 "register_operand" "")
(not:SI (match_operand:SI 1 "register_operand" "")))]
""
"")
 
(define_insn "*one_cmplsi2_h8300"
[(set (match_operand:SI 0 "register_operand" "=r")
(not:SI (match_operand:SI 1 "register_operand" "0")))]
"TARGET_H8300"
"not %w0\;not %x0\;not %y0\;not %z0"
[(set_attr "length" "8")])
 
(define_insn "*one_cmplsi2_h8300hs"
[(set (match_operand:SI 0 "h8300_dst_operand" "=rQ")
(not:SI (match_operand:SI 1 "h8300_dst_operand" "0")))]
"(TARGET_H8300H || TARGET_H8300S) && h8300_operands_match_p (operands)"
"not.l %S0"
[(set_attr "cc" "set_znv")
(set_attr "length_table" "unary")])
;; ----------------------------------------------------------------------
;; JUMP INSTRUCTIONS
;; ----------------------------------------------------------------------
 
;; Conditional jump instructions
 
(define_expand "ble"
[(match_operand 0 "" "")]
""
"h8300_expand_branch (LE, operands[0]); DONE;")
 
(define_expand "bleu"
[(match_operand 0 "" "")]
""
"h8300_expand_branch (LEU, operands[0]); DONE;")
 
(define_expand "bge"
[(match_operand 0 "" "")]
""
"h8300_expand_branch (GE, operands[0]); DONE;")
 
(define_expand "bgeu"
[(match_operand 0 "" "")]
""
"h8300_expand_branch (GEU, operands[0]); DONE;")
 
(define_expand "blt"
[(match_operand 0 "" "")]
""
"h8300_expand_branch (LT, operands[0]); DONE;")
 
(define_expand "bltu"
[(match_operand 0 "" "")]
""
"h8300_expand_branch (LTU, operands[0]); DONE;")
 
(define_expand "bgt"
[(match_operand 0 "" "")]
""
"h8300_expand_branch (GT, operands[0]); DONE;")
 
(define_expand "bgtu"
[(match_operand 0 "" "")]
""
"h8300_expand_branch (GTU, operands[0]); DONE;")
 
(define_expand "beq"
[(match_operand 0 "" "")]
""
"h8300_expand_branch (EQ, operands[0]); DONE;")
 
(define_expand "bne"
[(match_operand 0 "" "")]
""
"h8300_expand_branch (NE, operands[0]); DONE;")
 
(define_insn "branch_true"
[(set (pc)
(if_then_else (match_operator 1 "comparison_operator"
[(cc0) (const_int 0)])
(label_ref (match_operand 0 "" ""))
(pc)))]
""
"*
{
if ((cc_status.flags & CC_OVERFLOW_UNUSABLE) != 0
&& (GET_CODE (operands[1]) == GT
|| GET_CODE (operands[1]) == GE
|| GET_CODE (operands[1]) == LE
|| GET_CODE (operands[1]) == LT))
{
cc_status.flags &= ~CC_OVERFLOW_UNUSABLE;
return 0;
}
 
if (get_attr_length (insn) == 2)
return \"b%j1 %l0\";
else if (get_attr_length (insn) == 4)
return \"b%j1 %l0:16\";
else
return \"b%k1 .Lh8BR%=\;jmp @%l0\\n.Lh8BR%=:\";
}"
[(set_attr "type" "branch")
(set_attr "cc" "none")])
 
(define_insn "branch_false"
[(set (pc)
(if_then_else (match_operator 1 "comparison_operator"
[(cc0) (const_int 0)])
(pc)
(label_ref (match_operand 0 "" ""))))]
""
"*
{
if ((cc_status.flags & CC_OVERFLOW_UNUSABLE) != 0
&& (GET_CODE (operands[1]) == GT
|| GET_CODE (operands[1]) == GE
|| GET_CODE (operands[1]) == LE
|| GET_CODE (operands[1]) == LT))
{
cc_status.flags &= ~CC_OVERFLOW_UNUSABLE;
return 0;
}
 
if (get_attr_length (insn) == 2)
return \"b%k1 %l0\";
else if (get_attr_length (insn) == 4)
return \"b%k1 %l0:16\";
else
return \"b%j1 .Lh8BR%=\;jmp @%l0\\n.Lh8BR%=:\";
}"
[(set_attr "type" "branch")
(set_attr "cc" "none")])
 
(define_insn "*brabc"
[(set (pc)
(if_then_else
(eq (zero_extract (match_operand:QI 1 "bit_memory_operand" "WU")
(const_int 1)
(match_operand:QI 2 "immediate_operand" "n"))
(const_int 0))
(label_ref (match_operand 0 "" ""))
(pc)))]
"TARGET_H8300SX"
"*
{
switch (get_attr_length (insn)
- h8300_insn_length_from_table (insn, operands))
{
case 2:
return \"bra/bc %2,%R1,%l0\";
 
case 4:
return \"bra/bc %2,%R1,%l0:16\";
 
default:
return \"bra/bs %2,%R1,.Lh8BR%=\;jmp @%l0\\n.Lh8BR%=:\";
}
}"
[(set_attr "type" "bitbranch")
(set_attr "length_table" "bitbranch")
(set_attr "cc" "none")])
 
(define_insn "*brabs"
[(set (pc)
(if_then_else
(ne (zero_extract (match_operand:QI 1 "bit_memory_operand" "WU")
(const_int 1)
(match_operand:QI 2 "immediate_operand" "n"))
(const_int 0))
(label_ref (match_operand 0 "" ""))
(pc)))]
"TARGET_H8300SX"
"*
{
switch (get_attr_length (insn)
- h8300_insn_length_from_table (insn, operands))
{
case 2:
return \"bra/bs %2,%R1,%l0\";
 
case 4:
return \"bra/bs %2,%R1,%l0:16\";
 
default:
return \"bra/bc %2,%R1,.Lh8BR%=\;jmp @%l0\\n.Lh8BR%=:\";
}
}"
[(set_attr "type" "bitbranch")
(set_attr "length_table" "bitbranch")
(set_attr "cc" "none")])
 
;; Unconditional and other jump instructions.
 
(define_insn "jump"
[(set (pc)
(label_ref (match_operand 0 "" "")))]
""
"*
{
if (final_sequence != 0)
{
if (get_attr_length (insn) == 2)
return \"bra/s %l0\";
else
{
/* The branch isn't short enough to use bra/s. Output the
branch and delay slot in their normal order.
 
If this is a backward branch, it will now be branching two
bytes further than previously thought. The length-based
test for bra vs. jump is very conservative though, so the
branch will still be within range. */
rtvec vec;
int seen;
 
vec = XVEC (final_sequence, 0);
final_sequence = 0;
final_scan_insn (RTVEC_ELT (vec, 1), asm_out_file, optimize, 1, & seen);
final_scan_insn (RTVEC_ELT (vec, 0), asm_out_file, optimize, 1, & seen);
INSN_DELETED_P (RTVEC_ELT (vec, 1)) = 1;
return \"\";
}
}
else if (get_attr_length (insn) == 2)
return \"bra %l0\";
else if (get_attr_length (insn) == 4)
return \"bra %l0:16\";
else
return \"jmp @%l0\";
}"
[(set_attr "type" "branch")
(set (attr "delay_slot")
(if_then_else (ne (symbol_ref "TARGET_H8300SX") (const_int 0))
(const_string "jump")
(const_string "none")))
(set_attr "cc" "none")])
 
;; This is a define expand, because pointers may be either 16 or 32 bits.
 
(define_expand "tablejump"
[(parallel [(set (pc) (match_operand 0 "register_operand" ""))
(use (label_ref (match_operand 1 "" "")))])]
""
"")
 
(define_insn "*tablejump_h8300"
[(set (pc) (match_operand:HI 0 "register_operand" "r"))
(use (label_ref (match_operand 1 "" "")))]
"TARGET_H8300"
"jmp @%0"
[(set_attr "cc" "none")
(set_attr "length" "2")])
 
(define_insn "*tablejump_h8300hs_advanced"
[(set (pc) (match_operand:SI 0 "register_operand" "r"))
(use (label_ref (match_operand 1 "" "")))]
"(TARGET_H8300H || TARGET_H8300S) && !TARGET_NORMAL_MODE"
"jmp @%0"
[(set_attr "cc" "none")
(set_attr "length" "2")])
 
(define_insn "*tablejump_h8300hs_normal"
[(set (pc) (match_operand:HI 0 "register_operand" "r"))
(use (label_ref (match_operand 1 "" "")))]
"(TARGET_H8300H || TARGET_H8300S) && TARGET_NORMAL_MODE"
"jmp @%S0"
[(set_attr "cc" "none")
(set_attr "length" "2")])
 
;; This is a define expand, because pointers may be either 16 or 32 bits.
 
(define_expand "indirect_jump"
[(set (pc) (match_operand 0 "jump_address_operand" ""))]
""
"")
 
(define_insn "*indirect_jump_h8300"
[(set (pc) (match_operand:HI 0 "jump_address_operand" "Vr"))]
"TARGET_H8300"
"jmp @%0"
[(set_attr "cc" "none")
(set_attr "length" "2")])
 
(define_insn "*indirect_jump_h8300hs_advanced"
[(set (pc) (match_operand:SI 0 "jump_address_operand" "Vr"))]
"(TARGET_H8300H || TARGET_H8300S) && !TARGET_NORMAL_MODE"
"jmp @%0"
[(set_attr "cc" "none")
(set_attr "length" "2")])
 
(define_insn "*indirect_jump_h8300hs_normal"
[(set (pc) (match_operand:HI 0 "jump_address_operand" "Vr"))]
"(TARGET_H8300H || TARGET_H8300S) && TARGET_NORMAL_MODE"
"jmp @%S0"
[(set_attr "cc" "none")
(set_attr "length" "2")])
 
;; Call subroutine with no return value.
 
;; ??? Even though we use HImode here, this works on the H8/300H and H8S.
 
(define_insn "call"
[(call (match_operand:QI 0 "call_insn_operand" "or")
(match_operand:HI 1 "general_operand" "g"))]
""
"*
{
if (GET_CODE (XEXP (operands[0], 0)) == SYMBOL_REF
&& SYMBOL_REF_FLAG (XEXP (operands[0], 0)))
return \"jsr\\t@%0:8\";
else
return \"jsr\\t%0\";
}"
[(set_attr "type" "call")
(set (attr "length")
(if_then_else (match_operand:QI 0 "small_call_insn_operand" "")
(const_int 2)
(const_int 4)))])
 
;; Call subroutine, returning value in operand 0
;; (which must be a hard register).
 
;; ??? Even though we use HImode here, this works on the H8/300H and H8S.
 
(define_insn "call_value"
[(set (match_operand 0 "" "=r")
(call (match_operand:QI 1 "call_insn_operand" "or")
(match_operand:HI 2 "general_operand" "g")))]
""
"*
{
if (GET_CODE (XEXP (operands[1], 0)) == SYMBOL_REF
&& SYMBOL_REF_FLAG (XEXP (operands[1], 0)))
return \"jsr\\t@%1:8\";
else
return \"jsr\\t%1\";
}"
[(set_attr "type" "call")
(set (attr "length")
(if_then_else (match_operand:QI 0 "small_call_insn_operand" "")
(const_int 2)
(const_int 4)))])
 
(define_insn "nop"
[(const_int 0)]
""
"nop"
[(set_attr "cc" "none")
(set_attr "length" "2")])
;; ----------------------------------------------------------------------
;; PROLOGUE/EPILOGUE-RELATED INSTRUCTIONS
;; ----------------------------------------------------------------------
 
(define_expand "push_h8300"
[(set (mem:HI (pre_dec:HI (reg:HI SP_REG)))
(match_operand:HI 0 "register_operand" ""))]
"TARGET_H8300"
"")
 
(define_expand "push_h8300hs_advanced"
[(set (mem:SI (pre_dec:SI (reg:SI SP_REG)))
(match_operand:SI 0 "register_operand" ""))]
"TARGET_H8300H && TARGET_H8300S && !TARGET_NORMAL_MODE"
"")
 
(define_expand "push_h8300hs_normal"
[(set (mem:SI (pre_dec:HI (reg:HI SP_REG)))
(match_operand:SI 0 "register_operand" ""))]
"TARGET_H8300H && TARGET_H8300S && TARGET_NORMAL_MODE"
"")
 
(define_expand "pop_h8300"
[(set (match_operand:HI 0 "register_operand" "")
(mem:HI (post_inc:HI (reg:HI SP_REG))))]
"TARGET_H8300"
"")
 
(define_expand "pop_h8300hs_advanced"
[(set (match_operand:SI 0 "register_operand" "")
(mem:SI (post_inc:SI (reg:SI SP_REG))))]
"TARGET_H8300H && TARGET_H8300S && !TARGET_NORMAL_MODE"
"")
 
(define_expand "pop_h8300hs_normal"
[(set (match_operand:SI 0 "register_operand" "")
(mem:SI (post_inc:HI (reg:HI SP_REG))))]
"TARGET_H8300H && TARGET_H8300S && TARGET_NORMAL_MODE"
"")
 
(define_insn "ldm_h8300sx"
[(match_parallel 0 "h8300_ldm_parallel"
[(set (match_operand:SI 1 "register_operand" "")
(match_operand:SI 2 "memory_operand" ""))])]
"TARGET_H8300S"
{
operands[3] = SET_DEST (XVECEXP (operands[0], 0,
XVECLEN (operands[0], 0) - 2));
return "ldm.l\t@er7+,%S1-%S3";
}
[(set_attr "cc" "none")
(set_attr "length" "4")])
 
(define_insn "stm_h8300sx"
[(match_parallel 0 "h8300_stm_parallel"
[(set (match_operand:SI 1 "memory_operand" "")
(match_operand:SI 2 "register_operand" ""))])]
"TARGET_H8300S"
{
operands[3] = SET_SRC (XVECEXP (operands[0], 0,
XVECLEN (operands[0], 0) - 2));
return "stm.l\t%S2-%S3,@-er7";
}
[(set_attr "cc" "none")
(set_attr "length" "4")])
 
(define_insn "return_h8sx"
[(match_parallel 0 "h8300_return_parallel"
[(return)
(set (match_operand:SI 1 "register_operand" "")
(match_operand:SI 2 "memory_operand" ""))])]
"TARGET_H8300SX"
{
operands[3] = SET_DEST (XVECEXP (operands[0], 0,
XVECLEN (operands[0], 0) - 2));
if (h8300_current_function_interrupt_function_p ())
return "rte/l\t%S1-%S3";
else
return "rts/l\t%S1-%S3";
}
[(set_attr "cc" "none")
(set_attr "can_delay" "no")
(set_attr "length" "2")])
 
(define_expand "return"
[(return)]
"h8300_can_use_return_insn_p ()"
"")
 
(define_insn "*return_1"
[(return)]
"reload_completed"
"*
{
if (h8300_current_function_interrupt_function_p ())
return \"rte\";
else
return \"rts\";
}"
[(set_attr "cc" "none")
(set_attr "can_delay" "no")
(set_attr "length" "2")])
 
(define_expand "prologue"
[(const_int 0)]
""
"h8300_expand_prologue (); DONE;")
 
(define_expand "epilogue"
[(return)]
""
"h8300_expand_epilogue (); DONE;")
 
(define_insn "monitor_prologue"
[(unspec_volatile [(const_int 0)] UNSPEC_MONITOR)]
""
"*
{
if (TARGET_H8300)
return \"subs\\t#2,r7\;mov.w\\tr0,@-r7\;stc\\tccr,r0l\;mov.b\tr0l,@(2,r7)\;mov.w\\t@r7+,r0\;orc\t#128,ccr\";
else if (TARGET_H8300H)
return \"mov.l\\ter0,@-er7\;stc\\tccr,r0l\;mov.b\\tr0l,@(4,er7)\;mov.l\\t@er7+,er0\;orc\\t#128,ccr\";
else if (TARGET_H8300S)
return \"stc\texr,@-er7\;mov.l\\ter0,@-er7\;stc\tccr,r0l\;mov.b\tr0l,@(6,er7)\;mov.l\\t@er7+,er0\;orc\t#128,ccr\";
gcc_unreachable ();
}"
[(set_attr "length" "20")])
;; ----------------------------------------------------------------------
;; EXTEND INSTRUCTIONS
;; ----------------------------------------------------------------------
 
(define_expand "zero_extendqihi2"
[(set (match_operand:HI 0 "register_operand" "")
(zero_extend:HI (match_operand:QI 1 "general_operand_src" "")))]
""
"")
 
(define_insn "*zero_extendqihi2_h8300"
[(set (match_operand:HI 0 "register_operand" "=r,r")
(zero_extend:HI (match_operand:QI 1 "general_operand_src" "0,g>")))]
"TARGET_H8300"
"@
mov.b #0,%t0
#"
[(set_attr "length" "2,10")])
 
(define_insn "*zero_extendqihi2_h8300hs"
[(set (match_operand:HI 0 "register_operand" "=r,r")
(zero_extend:HI (match_operand:QI 1 "general_operand_src" "0,g>")))]
"TARGET_H8300H || TARGET_H8300S"
"@
extu.w %T0
#"
[(set_attr "length" "2,10")
(set_attr "cc" "set_znv,set_znv")])
 
;; Split the zero extension of a general operand (actually a memory
;; operand) into a load of the operand and the actual zero extension
;; so that 1) the length will be accurate, and 2) the zero extensions
;; appearing at the end of basic blocks may be merged.
 
(define_split
[(set (match_operand:HI 0 "register_operand" "")
(zero_extend:HI (match_operand:QI 1 "general_operand_src" "")))]
"reload_completed"
[(set (match_dup 2)
(match_dup 1))
(set (match_dup 0)
(zero_extend:HI (match_dup 2)))]
"operands[2] = gen_rtx_REG (QImode, REGNO (operands[0]));")
 
(define_expand "zero_extendqisi2"
[(set (match_operand:SI 0 "register_operand" "")
(zero_extend:SI (match_operand:QI 1 "general_operand_src" "")))]
""
{
if (TARGET_H8300SX)
operands[1] = force_reg (QImode, operands[1]);
})
 
(define_insn "*zero_extendqisi2_h8300"
[(set (match_operand:SI 0 "register_operand" "=r,r")
(zero_extend:SI (match_operand:QI 1 "general_operand_src" "0,g>")))]
"TARGET_H8300"
"@
mov.b #0,%x0\;sub.w %e0,%e0
mov.b %R1,%w0\;mov.b #0,%x0\;sub.w %e0,%e0"
[(set_attr "length" "4,8")])
 
(define_insn "*zero_extendqisi2_h8300hs"
[(set (match_operand:SI 0 "register_operand" "=r,r")
(zero_extend:SI (match_operand:QI 1 "general_operand_src" "0,g>")))]
"(TARGET_H8300H || TARGET_H8300S) && !TARGET_H8300SX"
"#")
 
(define_split
[(set (match_operand:SI 0 "register_operand" "")
(zero_extend:SI (match_operand:QI 1 "general_operand_src" "")))]
"(TARGET_H8300H || TARGET_H8300S) && !TARGET_H8300SX
&& reg_overlap_mentioned_p (operands[0], operands[1])
&& reload_completed"
[(set (match_dup 2)
(match_dup 1))
(set (match_dup 3)
(zero_extend:HI (match_dup 2)))
(set (match_dup 0)
(zero_extend:SI (match_dup 3)))]
"operands[2] = gen_lowpart (QImode, operands[0]);
operands[3] = gen_lowpart (HImode, operands[0]);")
 
(define_split
[(set (match_operand:SI 0 "register_operand" "")
(zero_extend:SI (match_operand:QI 1 "general_operand_src" "")))]
"(TARGET_H8300H || TARGET_H8300S) && !TARGET_H8300SX
&& !reg_overlap_mentioned_p (operands[0], operands[1])
&& reload_completed"
[(set (match_dup 0)
(const_int 0))
(set (strict_low_part (match_dup 2))
(match_dup 1))]
"operands[2] = gen_rtx_REG (QImode, REGNO (operands[0]));")
 
(define_insn "*zero_extendqisi2_h8sx"
[(set (match_operand:SI 0 "register_operand" "=r")
(zero_extend:SI (match_operand:QI 1 "register_operand" "0")))]
"TARGET_H8300SX"
"extu.l\t#2,%0"
[(set_attr "length" "2")
(set_attr "cc" "set_znv")])
 
(define_expand "zero_extendhisi2"
[(set (match_operand:SI 0 "register_operand" "")
(zero_extend:SI (match_operand:HI 1 "register_operand" "")))]
""
"")
 
;; %e prints the high part of a CONST_INT, not the low part. Arggh.
(define_insn "*zero_extendhisi2_h8300"
[(set (match_operand:SI 0 "register_operand" "=r,r,r")
(zero_extend:SI (match_operand:HI 1 "general_operand_src" "0,i,g>")))]
"TARGET_H8300"
"@
sub.w %e0,%e0
mov.w %f1,%f0\;sub.w %e0,%e0
mov.w %e1,%f0\;sub.w %e0,%e0"
[(set_attr "length" "2,4,6")])
 
(define_insn "*zero_extendhisi2_h8300hs"
[(set (match_operand:SI 0 "register_operand" "=r")
(zero_extend:SI (match_operand:HI 1 "register_operand" "0")))]
"TARGET_H8300H || TARGET_H8300S"
"extu.l %S0"
[(set_attr "length" "2")
(set_attr "cc" "set_znv")])
 
(define_expand "extendqihi2"
[(set (match_operand:HI 0 "register_operand" "")
(sign_extend:HI (match_operand:QI 1 "register_operand" "")))]
""
"")
 
(define_insn "*extendqihi2_h8300"
[(set (match_operand:HI 0 "register_operand" "=r,r")
(sign_extend:HI (match_operand:QI 1 "general_operand_src" "0,g>")))]
"TARGET_H8300"
"@
bld #7,%s0\;subx %t0,%t0
mov.b %R1,%s0\;bld #7,%s0\;subx %t0,%t0"
[(set_attr "length" "4,8")])
 
(define_insn "*extendqihi2_h8300hs"
[(set (match_operand:HI 0 "register_operand" "=r")
(sign_extend:HI (match_operand:QI 1 "register_operand" "0")))]
"TARGET_H8300H || TARGET_H8300S"
"exts.w %T0"
[(set_attr "length" "2")
(set_attr "cc" "set_znv")])
 
(define_expand "extendqisi2"
[(set (match_operand:SI 0 "register_operand" "")
(sign_extend:SI (match_operand:QI 1 "register_operand" "")))]
""
"")
 
(define_insn "*extendqisi2_h8300"
[(set (match_operand:SI 0 "register_operand" "=r,r")
(sign_extend:SI (match_operand:QI 1 "general_operand_src" "0,g>")))]
"TARGET_H8300"
"@
bld #7,%w0\;subx %x0,%x0\;subx %y0,%y0\;subx %z0,%z0
mov.b %R1,%w0\;bld #7,%w0\;subx %x0,%x0\;subx %y0,%y0\;subx %z0,%z0"
[(set_attr "length" "8,12")])
 
;; The following pattern is needed because without the pattern, the
;; combiner would split (sign_extend:SI (reg:QI)) into two 24-bit
;; shifts, one ashift and one ashiftrt.
 
(define_insn_and_split "*extendqisi2_h8300hs"
[(set (match_operand:SI 0 "register_operand" "=r")
(sign_extend:SI (match_operand:QI 1 "register_operand" "0")))]
"(TARGET_H8300H || TARGET_H8300S) && !TARGET_H8300SX"
"#"
"&& reload_completed"
[(set (match_dup 2)
(sign_extend:HI (match_dup 1)))
(set (match_dup 0)
(sign_extend:SI (match_dup 2)))]
"operands[2] = gen_rtx_REG (HImode, REGNO (operands[0]));")
 
(define_insn "*extendqisi2_h8sx"
[(set (match_operand:SI 0 "register_operand" "=r")
(sign_extend:SI (match_operand:QI 1 "register_operand" "0")))]
"TARGET_H8300SX"
"exts.l\t#2,%0"
[(set_attr "length" "2")
(set_attr "cc" "set_znv")])
 
(define_expand "extendhisi2"
[(set (match_operand:SI 0 "register_operand" "")
(sign_extend:SI (match_operand:HI 1 "register_operand" "")))]
""
"")
 
(define_insn "*extendhisi2_h8300"
[(set (match_operand:SI 0 "register_operand" "=r,r")
(sign_extend:SI (match_operand:HI 1 "general_operand_src" "0,g>")))]
"TARGET_H8300"
"@
bld #7,%x0\;subx %y0,%y0\;subx %z0,%z0
mov.w %T1,%f0\;bld #7,%x0\;subx %y0,%y0\;subx %z0,%z0"
[(set_attr "length" "6,10")])
 
(define_insn "*extendhisi2_h8300hs"
[(set (match_operand:SI 0 "register_operand" "=r")
(sign_extend:SI (match_operand:HI 1 "register_operand" "0")))]
"TARGET_H8300H || TARGET_H8300S"
"exts.l %S0"
[(set_attr "length" "2")
(set_attr "cc" "set_znv")])
;; ----------------------------------------------------------------------
;; SHIFTS
;; ----------------------------------------------------------------------
;;
;; We make some attempt to provide real efficient shifting. One example is
;; doing an 8 bit shift of a 16 bit value by moving a byte reg into the other
;; reg and moving 0 into the former reg.
;;
;; We also try to achieve this in a uniform way. IE: We don't try to achieve
;; this in both rtl and at insn emit time. Ideally, we'd use rtl as that would
;; give the optimizer more cracks at the code. However, we wish to do things
;; like optimizing shifting the sign bit to bit 0 by rotating the other way.
;; There is rtl to handle this (rotate + and), but the H8/300 doesn't handle
;; 16 bit rotates. Also, if we emit complicated rtl, combine may not be able
;; to detect cases it can optimize.
;;
;; For these and other fuzzy reasons, I've decided to go the less pretty but
;; easier "do it at insn emit time" route.
 
;; QI BIT SHIFTS
 
(define_expand "ashlqi3"
[(set (match_operand:QI 0 "register_operand" "")
(ashift:QI (match_operand:QI 1 "register_operand" "")
(match_operand:QI 2 "nonmemory_operand" "")))]
""
"if (expand_a_shift (QImode, ASHIFT, operands)) DONE;")
 
(define_expand "ashrqi3"
[(set (match_operand:QI 0 "register_operand" "")
(ashiftrt:QI (match_operand:QI 1 "register_operand" "")
(match_operand:QI 2 "nonmemory_operand" "")))]
""
"if (expand_a_shift (QImode, ASHIFTRT, operands)) DONE;")
 
(define_expand "lshrqi3"
[(set (match_operand:QI 0 "register_operand" "")
(lshiftrt:QI (match_operand:QI 1 "register_operand" "")
(match_operand:QI 2 "nonmemory_operand" "")))]
""
"if (expand_a_shift (QImode, LSHIFTRT, operands)) DONE;")
 
(define_insn ""
[(set (match_operand:QI 0 "h8300_dst_operand" "=rQ")
(match_operator:QI 3 "h8sx_unary_shift_operator"
[(match_operand:QI 1 "h8300_dst_operand" "0")
(match_operand:QI 2 "const_int_operand" "")]))]
"h8300_operands_match_p (operands)"
{ return output_h8sx_shift (operands, 'b', 'X'); }
[(set_attr "length_table" "unary")
(set_attr "cc" "set_znv")])
 
(define_insn ""
[(set (match_operand:QI 0 "register_operand" "=r")
(match_operator:QI 3 "h8sx_binary_shift_operator"
[(match_operand:QI 1 "register_operand" "0")
(match_operand:QI 2 "nonmemory_operand" "r P3>X")]))]
""
{ return output_h8sx_shift (operands, 'b', 'X'); }
[(set_attr "length" "4")
(set_attr "cc" "set_znv")])
 
(define_insn "*shiftqi"
[(set (match_operand:QI 0 "register_operand" "=r,r")
(match_operator:QI 3 "nshift_operator"
[ (match_operand:QI 1 "register_operand" "0,0")
(match_operand:QI 2 "nonmemory_operand" "R,rn")]))
(clobber (match_scratch:QI 4 "=X,&r"))]
""
"* return output_a_shift (operands);"
[(set (attr "length")
(symbol_ref "compute_a_shift_length (insn, operands)"))
(set (attr "cc")
(symbol_ref "compute_a_shift_cc (insn, operands)"))])
 
;; HI BIT SHIFTS
 
(define_expand "ashlhi3"
[(set (match_operand:HI 0 "register_operand" "")
(ashift:HI (match_operand:HI 1 "register_operand" "")
(match_operand:QI 2 "nonmemory_operand" "")))]
""
"if (expand_a_shift (HImode, ASHIFT, operands)) DONE;")
 
(define_expand "lshrhi3"
[(set (match_operand:HI 0 "register_operand" "")
(lshiftrt:HI (match_operand:HI 1 "register_operand" "")
(match_operand:QI 2 "nonmemory_operand" "")))]
""
"if (expand_a_shift (HImode, LSHIFTRT, operands)) DONE;")
 
(define_expand "ashrhi3"
[(set (match_operand:HI 0 "register_operand" "")
(ashiftrt:HI (match_operand:HI 1 "register_operand" "")
(match_operand:QI 2 "nonmemory_operand" "")))]
""
"if (expand_a_shift (HImode, ASHIFTRT, operands)) DONE;")
 
(define_insn ""
[(set (match_operand:HI 0 "h8300_dst_operand" "=rQ")
(match_operator:HI 3 "h8sx_unary_shift_operator"
[(match_operand:HI 1 "h8300_dst_operand" "0")
(match_operand:QI 2 "const_int_operand" "")]))]
"h8300_operands_match_p (operands)"
{ return output_h8sx_shift (operands, 'w', 'T'); }
[(set_attr "length_table" "unary")
(set_attr "cc" "set_znv")])
 
(define_insn ""
[(set (match_operand:HI 0 "register_operand" "=r")
(match_operator:HI 3 "h8sx_binary_shift_operator"
[(match_operand:HI 1 "register_operand" "0")
(match_operand:QI 2 "nonmemory_operand" "r P4>X")]))]
""
{ return output_h8sx_shift (operands, 'w', 'T'); }
[(set_attr "length" "4")
(set_attr "cc" "set_znv")])
 
(define_insn "*shifthi"
[(set (match_operand:HI 0 "register_operand" "=r,r")
(match_operator:HI 3 "nshift_operator"
[ (match_operand:HI 1 "register_operand" "0,0")
(match_operand:QI 2 "nonmemory_operand" "S,rn")]))
(clobber (match_scratch:QI 4 "=X,&r"))]
""
"* return output_a_shift (operands);"
[(set (attr "length")
(symbol_ref "compute_a_shift_length (insn, operands)"))
(set (attr "cc")
(symbol_ref "compute_a_shift_cc (insn, operands)"))])
 
;; SI BIT SHIFTS
 
(define_expand "ashlsi3"
[(set (match_operand:SI 0 "register_operand" "")
(ashift:SI (match_operand:SI 1 "register_operand" "")
(match_operand:QI 2 "nonmemory_operand" "")))]
""
"if (expand_a_shift (SImode, ASHIFT, operands)) DONE;")
 
(define_expand "lshrsi3"
[(set (match_operand:SI 0 "register_operand" "")
(lshiftrt:SI (match_operand:SI 1 "register_operand" "")
(match_operand:QI 2 "nonmemory_operand" "")))]
""
"if (expand_a_shift (SImode, LSHIFTRT, operands)) DONE;")
 
(define_expand "ashrsi3"
[(set (match_operand:SI 0 "register_operand" "")
(ashiftrt:SI (match_operand:SI 1 "register_operand" "")
(match_operand:QI 2 "nonmemory_operand" "")))]
""
"if (expand_a_shift (SImode, ASHIFTRT, operands)) DONE;")
 
(define_insn ""
[(set (match_operand:SI 0 "h8300_dst_operand" "=rQ")
(match_operator:SI 3 "h8sx_unary_shift_operator"
[(match_operand:SI 1 "h8300_dst_operand" "0")
(match_operand:QI 2 "const_int_operand" "")]))]
"h8300_operands_match_p (operands)"
{ return output_h8sx_shift (operands, 'l', 'S'); }
[(set_attr "length_table" "unary")
(set_attr "cc" "set_znv")])
 
(define_insn ""
[(set (match_operand:SI 0 "register_operand" "=r")
(match_operator:SI 3 "h8sx_binary_shift_operator"
[(match_operand:SI 1 "register_operand" "0")
(match_operand:QI 2 "nonmemory_operand" "r P5>X")]))]
""
{ return output_h8sx_shift (operands, 'l', 'S'); }
[(set_attr "length" "4")
(set_attr "cc" "set_znv")])
 
(define_insn "*shiftsi"
[(set (match_operand:SI 0 "register_operand" "=r,r")
(match_operator:SI 3 "nshift_operator"
[ (match_operand:SI 1 "register_operand" "0,0")
(match_operand:QI 2 "nonmemory_operand" "T,rn")]))
(clobber (match_scratch:QI 4 "=X,&r"))]
""
"* return output_a_shift (operands);"
[(set (attr "length")
(symbol_ref "compute_a_shift_length (insn, operands)"))
(set (attr "cc")
(symbol_ref "compute_a_shift_cc (insn, operands)"))])
 
;; Split a variable shift into a loop. If the register containing
;; the shift count dies, then we just use that register.
 
(define_split
[(set (match_operand 0 "register_operand" "")
(match_operator 2 "nshift_operator"
[(match_dup 0)
(match_operand:QI 1 "register_operand" "")]))
(clobber (match_operand:QI 3 "register_operand" ""))]
"flow2_completed
&& find_regno_note (insn, REG_DEAD, REGNO (operands[1]))"
[(set (cc0)
(match_dup 1))
(set (pc)
(if_then_else (le (cc0) (const_int 0))
(label_ref (match_dup 5))
(pc)))
(match_dup 4)
(parallel
[(set (match_dup 0)
(match_op_dup 2 [(match_dup 0) (const_int 1)]))
(clobber (scratch:QI))])
(set (match_dup 1)
(plus:QI (match_dup 1) (const_int -1)))
(set (cc0)
(match_dup 1))
(set (pc)
(if_then_else (ne (cc0) (const_int 0))
(label_ref (match_dup 4))
(pc)))
(match_dup 5)]
"operands[4] = gen_label_rtx ();
operands[5] = gen_label_rtx ();")
 
(define_split
[(set (match_operand 0 "register_operand" "")
(match_operator 2 "nshift_operator"
[(match_dup 0)
(match_operand:QI 1 "register_operand" "")]))
(clobber (match_operand:QI 3 "register_operand" ""))]
"flow2_completed
&& !find_regno_note (insn, REG_DEAD, REGNO (operands[1]))"
[(set (match_dup 3)
(match_dup 1))
(set (cc0)
(match_dup 3))
(set (pc)
(if_then_else (le (cc0) (const_int 0))
(label_ref (match_dup 5))
(pc)))
(match_dup 4)
(parallel
[(set (match_dup 0)
(match_op_dup 2 [(match_dup 0) (const_int 1)]))
(clobber (scratch:QI))])
(set (match_dup 3)
(plus:QI (match_dup 3) (const_int -1)))
(set (cc0)
(match_dup 3))
(set (pc)
(if_then_else (ne (cc0) (const_int 0))
(label_ref (match_dup 4))
(pc)))
(match_dup 5)]
"operands[4] = gen_label_rtx ();
operands[5] = gen_label_rtx ();")
;; ----------------------------------------------------------------------
;; ROTATIONS
;; ----------------------------------------------------------------------
 
(define_expand "rotlqi3"
[(set (match_operand:QI 0 "register_operand" "")
(rotate:QI (match_operand:QI 1 "register_operand" "")
(match_operand:QI 2 "nonmemory_operand" "")))]
""
"if (expand_a_rotate (operands)) DONE;")
 
(define_insn "rotlqi3_1"
[(set (match_operand:QI 0 "register_operand" "=r")
(rotate:QI (match_operand:QI 1 "register_operand" "0")
(match_operand:QI 2 "immediate_operand" "")))]
""
"* return output_a_rotate (ROTATE, operands);"
[(set (attr "length")
(symbol_ref "compute_a_rotate_length (operands)"))])
 
(define_expand "rotlhi3"
[(set (match_operand:HI 0 "register_operand" "")
(rotate:HI (match_operand:HI 1 "register_operand" "")
(match_operand:QI 2 "nonmemory_operand" "")))]
""
"if (expand_a_rotate (operands)) DONE;")
 
(define_insn "rotlhi3_1"
[(set (match_operand:HI 0 "register_operand" "=r")
(rotate:HI (match_operand:HI 1 "register_operand" "0")
(match_operand:QI 2 "immediate_operand" "")))]
""
"* return output_a_rotate (ROTATE, operands);"
[(set (attr "length")
(symbol_ref "compute_a_rotate_length (operands)"))])
 
(define_expand "rotlsi3"
[(set (match_operand:SI 0 "register_operand" "")
(rotate:SI (match_operand:SI 1 "register_operand" "")
(match_operand:QI 2 "nonmemory_operand" "")))]
"TARGET_H8300H || TARGET_H8300S"
"if (expand_a_rotate (operands)) DONE;")
 
(define_insn "rotlsi3_1"
[(set (match_operand:SI 0 "register_operand" "=r")
(rotate:SI (match_operand:SI 1 "register_operand" "0")
(match_operand:QI 2 "immediate_operand" "")))]
"TARGET_H8300H || TARGET_H8300S"
"* return output_a_rotate (ROTATE, operands);"
[(set (attr "length")
(symbol_ref "compute_a_rotate_length (operands)"))])
;; -----------------------------------------------------------------
;; BIT FIELDS
;; -----------------------------------------------------------------
;; The H8/300 has given 1/8th of its opcode space to bitfield
;; instructions so let's use them as well as we can.
 
;; You'll never believe all these patterns perform one basic action --
;; load a bit from the source, optionally invert the bit, then store it
;; in the destination (which is known to be zero).
;;
;; Combine obviously need some work to better identify this situation and
;; canonicalize the form better.
 
;;
;; Normal loads with a 16bit destination.
;;
 
(define_insn ""
[(set (match_operand:HI 0 "register_operand" "=&r")
(zero_extract:HI (match_operand:HI 1 "register_operand" "r")
(const_int 1)
(match_operand:HI 2 "immediate_operand" "n")))]
"TARGET_H8300"
"sub.w %0,%0\;bld %Z2,%Y1\;bst #0,%X0"
[(set_attr "length" "6")])
 
;;
;; Inverted loads with a 16bit destination.
;;
 
(define_insn ""
[(set (match_operand:HI 0 "register_operand" "=&r")
(zero_extract:HI (xor:HI (match_operand:HI 1 "register_operand" "r")
(match_operand:HI 3 "const_int_operand" "n"))
(const_int 1)
(match_operand:HI 2 "const_int_operand" "n")))]
"TARGET_H8300
&& (1 << INTVAL (operands[2])) == INTVAL (operands[3])"
"sub.w %0,%0\;bild %Z2,%Y1\;bst #0,%X0"
[(set_attr "length" "8")])
 
;;
;; Normal loads with a 32bit destination.
;;
 
(define_insn "*extzv_1_r_h8300"
[(set (match_operand:SI 0 "register_operand" "=&r")
(zero_extract:SI (match_operand:HI 1 "register_operand" "r")
(const_int 1)
(match_operand 2 "const_int_operand" "n")))]
"TARGET_H8300
&& INTVAL (operands[2]) < 16"
"* return output_simode_bld (0, operands);"
[(set_attr "length" "8")])
 
(define_insn "*extzv_1_r_h8300hs"
[(set (match_operand:SI 0 "register_operand" "=r,r")
(zero_extract:SI (match_operand:SI 1 "register_operand" "?0,r")
(const_int 1)
(match_operand 2 "const_int_operand" "n,n")))]
"(TARGET_H8300H || TARGET_H8300S)
&& INTVAL (operands[2]) < 16"
"* return output_simode_bld (0, operands);"
[(set_attr "cc" "set_znv,set_znv")
(set_attr "length" "8,6")])
 
;;
;; Inverted loads with a 32bit destination.
;;
 
(define_insn "*extzv_1_r_inv_h8300"
[(set (match_operand:SI 0 "register_operand" "=&r")
(zero_extract:SI (xor:HI (match_operand:HI 1 "register_operand" "r")
(match_operand:HI 3 "const_int_operand" "n"))
(const_int 1)
(match_operand 2 "const_int_operand" "n")))]
"TARGET_H8300
&& INTVAL (operands[2]) < 16
&& (1 << INTVAL (operands[2])) == INTVAL (operands[3])"
"* return output_simode_bld (1, operands);"
[(set_attr "length" "8")])
 
(define_insn "*extzv_1_r_inv_h8300hs"
[(set (match_operand:SI 0 "register_operand" "=r,r")
(zero_extract:SI (xor:SI (match_operand:SI 1 "register_operand" "?0,r")
(match_operand 3 "const_int_operand" "n,n"))
(const_int 1)
(match_operand 2 "const_int_operand" "n,n")))]
"(TARGET_H8300H || TARGET_H8300S)
&& INTVAL (operands[2]) < 16
&& (1 << INTVAL (operands[2])) == INTVAL (operands[3])"
"* return output_simode_bld (1, operands);"
[(set_attr "cc" "set_znv,set_znv")
(set_attr "length" "8,6")])
 
(define_expand "insv"
[(set (zero_extract:HI (match_operand:HI 0 "general_operand" "")
(match_operand:HI 1 "general_operand" "")
(match_operand:HI 2 "general_operand" ""))
(match_operand:HI 3 "general_operand" ""))]
"TARGET_H8300 || TARGET_H8300SX"
"
{
if (TARGET_H8300SX)
{
if (GET_CODE (operands[1]) == CONST_INT
&& GET_CODE (operands[2]) == CONST_INT
&& INTVAL (operands[1]) <= 8
&& INTVAL (operands[2]) >= 0
&& INTVAL (operands[1]) + INTVAL (operands[2]) <= 8
&& memory_operand (operands[0], GET_MODE (operands[0])))
{
/* If the source operand is zero, it's better to use AND rather
than BFST. Likewise OR if the operand is all ones. */
if (GET_CODE (operands[3]) == CONST_INT)
{
HOST_WIDE_INT mask = (1 << INTVAL (operands[1])) - 1;
if ((INTVAL (operands[3]) & mask) == 0)
FAIL;
if ((INTVAL (operands[3]) & mask) == mask)
FAIL;
}
if (! bit_memory_operand (operands[0], GET_MODE (operands[0])))
{
if (no_new_pseudos)
FAIL;
operands[0] =
replace_equiv_address (operands[0],
force_reg (Pmode,
XEXP (operands[0], 0)));
}
operands[3] = gen_lowpart (QImode, operands[3]);
if (! operands[3])
FAIL;
if (! register_operand (operands[3], QImode))
{
if (no_new_pseudos)
FAIL;
operands[3] = force_reg (QImode, operands[3]);
}
emit_insn (gen_bfst (adjust_address (operands[0], QImode, 0),
operands[3], operands[1], operands[2]));
DONE;
}
 
FAIL;
}
 
/* We only have single bit bit-field instructions. */
if (INTVAL (operands[1]) != 1)
FAIL;
 
/* For now, we don't allow memory operands. */
if (GET_CODE (operands[0]) == MEM
|| GET_CODE (operands[3]) == MEM)
FAIL;
}")
 
(define_insn ""
[(set (zero_extract:HI (match_operand:HI 0 "register_operand" "+r")
(const_int 1)
(match_operand:HI 1 "immediate_operand" "n"))
(match_operand:HI 2 "register_operand" "r"))]
""
"bld #0,%R2\;bst %Z1,%Y0 ; i1"
[(set_attr "length" "4")])
 
(define_expand "extzv"
[(set (match_operand:HI 0 "register_operand" "")
(zero_extract:HI (match_operand:HI 1 "bit_operand" "")
(match_operand:HI 2 "general_operand" "")
(match_operand:HI 3 "general_operand" "")))]
"TARGET_H8300 || TARGET_H8300SX"
"
{
if (TARGET_H8300SX)
{
if (GET_CODE (operands[2]) == CONST_INT
&& GET_CODE (operands[3]) == CONST_INT
&& INTVAL (operands[2]) <= 8
&& INTVAL (operands[3]) >= 0
&& INTVAL (operands[2]) + INTVAL (operands[3]) <= 8
&& memory_operand (operands[1], QImode))
{
rtx temp;
 
/* Optimize the case where we're extracting into a paradoxical
subreg. It's only necessary to extend to the inner reg. */
if (GET_CODE (operands[0]) == SUBREG
&& subreg_lowpart_p (operands[0])
&& (GET_MODE_SIZE (GET_MODE (SUBREG_REG (operands[0])))
< GET_MODE_SIZE (GET_MODE (operands[0])))
&& (GET_MODE_CLASS (GET_MODE (SUBREG_REG (operands[0])))
== MODE_INT))
operands[0] = SUBREG_REG (operands[0]);
 
if (no_new_pseudos)
temp = gen_lowpart (QImode, operands[0]);
else
temp = gen_reg_rtx (QImode);
if (! temp)
FAIL;
if (! bit_memory_operand (operands[1], QImode))
{
if (no_new_pseudos)
FAIL;
operands[1] =
replace_equiv_address (operands[1],
force_reg (Pmode,
XEXP (operands[1], 0)));
}
emit_insn (gen_bfld (temp, operands[1], operands[2], operands[3]));
convert_move (operands[0], temp, 1);
DONE;
}
FAIL;
}
 
/* We only have single bit bit-field instructions. */
if (INTVAL (operands[2]) != 1)
FAIL;
 
/* For now, we don't allow memory operands. */
if (GET_CODE (operands[1]) == MEM)
FAIL;
}")
 
;; BAND, BOR, and BXOR patterns
 
(define_insn ""
[(set (match_operand:HI 0 "bit_operand" "=Ur")
(match_operator:HI 4 "bit_operator"
[(zero_extract:HI (match_operand:HI 1 "register_operand" "r")
(const_int 1)
(match_operand:HI 2 "immediate_operand" "n"))
(match_operand:HI 3 "bit_operand" "0")]))]
""
"bld %Z2,%Y1\;b%c4 #0,%R0\;bst #0,%R0; bl1"
[(set_attr "length" "6")])
 
(define_insn ""
[(set (match_operand:HI 0 "bit_operand" "=Ur")
(match_operator:HI 5 "bit_operator"
[(zero_extract:HI (match_operand:HI 1 "register_operand" "r")
(const_int 1)
(match_operand:HI 2 "immediate_operand" "n"))
(zero_extract:HI (match_operand:HI 3 "register_operand" "r")
(const_int 1)
(match_operand:HI 4 "immediate_operand" "n"))]))]
""
"bld %Z2,%Y1\;b%c5 %Z4,%Y3\;bst #0,%R0; bl3"
[(set_attr "length" "6")])
 
(define_insn "bfld"
[(set (match_operand:QI 0 "register_operand" "=r")
(zero_extract:QI (match_operand:QI 1 "bit_memory_operand" "WU")
(match_operand:QI 2 "immediate_operand" "n")
(match_operand:QI 3 "immediate_operand" "n")))]
"TARGET_H8300SX && INTVAL (operands[2]) + INTVAL (operands[3]) <= 8"
"*
{
operands[2] = GEN_INT ((1 << (INTVAL (operands[2]) + INTVAL (operands[3])))
- (1 << INTVAL (operands[3])));
return \"bfld %2,%1,%R0\";
}"
[(set_attr "cc" "none_0hit")
(set_attr "length_table" "bitfield")])
 
(define_insn "bfst"
[(set (zero_extract:QI (match_operand:QI 0 "bit_memory_operand" "+WU")
(match_operand:QI 2 "immediate_operand" "n")
(match_operand:QI 3 "immediate_operand" "n"))
(match_operand:QI 1 "register_operand" "r"))]
"TARGET_H8300SX && INTVAL (operands[2]) + INTVAL (operands[3]) <= 8"
"*
{
operands[2] = GEN_INT ((1 << (INTVAL (operands[2]) + INTVAL (operands[3])))
- (1 << INTVAL (operands[3])));
return \"bfst %R1,%2,%0\";
}"
[(set_attr "cc" "none_0hit")
(set_attr "length_table" "bitfield")])
 
(define_expand "seq"
[(set (match_operand:HI 0 "register_operand" "")
(eq:HI (cc0) (const_int 0)))]
"TARGET_H8300SX"
"")
 
(define_expand "sne"
[(set (match_operand:HI 0 "register_operand" "")
(ne:HI (cc0) (const_int 0)))]
"TARGET_H8300SX"
"")
 
(define_insn "*bstzhireg"
[(set (match_operand:HI 0 "register_operand" "=r")
(match_operator:HI 1 "eqne_operator" [(cc0) (const_int 0)]))]
"TARGET_H8300SX"
"mulu.w #0,%T0\;b%k1 .Lh8BR%=\;inc.w #1,%T0\\n.Lh8BR%=:"
[(set_attr "cc" "clobber")])
 
(define_insn_and_split "*cmpstz"
[(set (zero_extract:QI
(match_operand:QI 0 "bit_memory_operand" "+WU,+WU")
(const_int 1)
(match_operand:QI 1 "immediate_operand" "n,n"))
(match_operator:QI
2 "eqne_operator"
[(match_operand 3 "h8300_dst_operand" "r,rQ")
(match_operand 4 "h8300_src_operand" "I,rQi")]))]
"TARGET_H8300SX
&& (GET_MODE (operands[3]) == GET_MODE (operands[4])
|| GET_CODE (operands[4]) == CONST_INT)
&& GET_MODE_CLASS (GET_MODE (operands[3])) == MODE_INT
&& GET_MODE_SIZE (GET_MODE (operands[3])) <= 4"
"#"
"reload_completed"
[(set (cc0) (match_dup 5))
(set (zero_extract:QI (match_dup 0) (const_int 1) (match_dup 1))
(match_op_dup:QI 2 [(cc0) (const_int 0)]))]
"
{
if (operands[4] == const0_rtx && GET_CODE (operands[3]) == REG)
operands[5] = operands[3];
else
operands[5] = gen_rtx_COMPARE (VOIDmode, operands[3], operands[4]);
}"
[(set_attr "cc" "set_znv,compare")])
(define_insn "*bstz"
[(set (zero_extract:QI (match_operand:QI 0 "bit_memory_operand" "+WU")
(const_int 1)
(match_operand:QI 1 "immediate_operand" "n"))
(eq:QI (cc0) (const_int 0)))]
"TARGET_H8300SX && reload_completed"
"bstz %1,%0"
[(set_attr "cc" "none_0hit")
(set_attr "length_table" "unary")])
 
(define_insn "*bistz"
[(set (zero_extract:QI (match_operand:QI 0 "bit_memory_operand" "+WU")
(const_int 1)
(match_operand:QI 1 "immediate_operand" "n"))
(ne:QI (cc0) (const_int 0)))]
"TARGET_H8300SX && reload_completed"
"bistz %1,%0"
[(set_attr "cc" "none_0hit")
(set_attr "length_table" "unary")])
 
(define_insn_and_split "*cmpcondbset"
[(set (match_operand:QI 0 "nonimmediate_operand" "=WU,WU")
(if_then_else:QI
(match_operator
1 "eqne_operator"
[(match_operand 2 "h8300_dst_operand" "r,rQ")
(match_operand 3 "h8300_src_operand" "I,rQi")])
(ior:QI
(match_operand:QI 4 "bit_memory_operand" "0,0")
(match_operand:QI 5 "single_one_operand" "n,n"))
(match_dup 4)))]
"TARGET_H8300SX"
"#"
"reload_completed"
[(set (cc0) (match_dup 6))
(set (match_dup 0)
(if_then_else:QI
(match_op_dup 1 [(cc0) (const_int 0)])
(ior:QI (match_dup 4) (match_dup 5)) (match_dup 4)))]
"
{
if (operands[3] == const0_rtx && GET_CODE (operands[2]) == REG)
operands[6] = operands[2];
else
operands[6] = gen_rtx_COMPARE (VOIDmode, operands[2], operands[3]);
}"
[(set_attr "cc" "set_znv,compare")])
(define_insn "*condbset"
[(set (match_operand:QI 0 "bit_memory_operand" "=WU")
(if_then_else:QI
(match_operator:QI 2 "eqne_operator"
[(cc0) (const_int 0)])
(ior:QI
(match_operand:QI 3 "bit_memory_operand" "0")
(match_operand:QI 1 "single_one_operand" "n"))
(match_dup 3)))]
"TARGET_H8300SX && reload_completed"
"bset/%j2\t%V1,%0"
[(set_attr "cc" "none_0hit")
(set_attr "length_table" "logicb")])
 
(define_insn_and_split "*cmpcondbclr"
[(set (match_operand:QI 0 "nonimmediate_operand" "=WU,WU")
(if_then_else:QI
(match_operator
1 "eqne_operator"
[(match_operand 2 "h8300_dst_operand" "r,rQ")
(match_operand 3 "h8300_src_operand" "I,rQi")])
(and:QI
(match_operand:QI 4 "bit_memory_operand" "0,0")
(match_operand:QI 5 "single_zero_operand" "n,n"))
(match_dup 4)))]
"TARGET_H8300SX"
"#"
"reload_completed"
[(set (cc0) (match_dup 6))
(set (match_dup 0)
(if_then_else:QI
(match_op_dup 1 [(cc0) (const_int 0)])
(and:QI (match_dup 4) (match_dup 5)) (match_dup 4)))]
"
{
if (operands[3] == const0_rtx && GET_CODE (operands[2]) == REG)
operands[6] = operands[2];
else
operands[6] = gen_rtx_COMPARE (VOIDmode, operands[2], operands[3]);
}"
[(set_attr "cc" "set_znv,compare")])
(define_insn "*condbclr"
[(set (match_operand:QI 0 "bit_memory_operand" "=WU")
(if_then_else:QI
(match_operator:QI 2 "eqne_operator"
[(cc0) (const_int 0)])
(and:QI
(match_operand:QI 3 "bit_memory_operand" "0")
(match_operand:QI 1 "single_zero_operand" "n"))
(match_dup 3)))]
"TARGET_H8300SX && reload_completed"
"bclr/%j2\t%W1,%0"
[(set_attr "cc" "none_0hit")
(set_attr "length_table" "logicb")])
 
(define_insn_and_split "*cmpcondbsetreg"
[(set (match_operand:QI 0 "nonimmediate_operand" "=WU,WU")
(if_then_else:QI
(match_operator
1 "eqne_operator"
[(match_operand 2 "h8300_dst_operand" "r,rQ")
(match_operand 3 "h8300_src_operand" "I,rQi")])
(ior:QI
(match_operand:QI 4 "bit_memory_operand" "0,0")
(ashift:QI (const_int 1)
(match_operand:QI 5 "register_operand" "r,r")))
(match_dup 4)))]
"TARGET_H8300SX"
"#"
"reload_completed"
[(set (cc0) (match_dup 6))
(set (match_dup 0)
(if_then_else:QI
(match_op_dup 1 [(cc0) (const_int 0)])
(ior:QI (match_dup 4)
(ashift:QI (const_int 1)
(match_operand:QI 5 "register_operand" "r,r")))
(match_dup 4)))]
"
{
if (operands[3] == const0_rtx && GET_CODE (operands[2]) == REG)
operands[6] = operands[2];
else
operands[6] = gen_rtx_COMPARE (VOIDmode, operands[2], operands[3]);
}"
[(set_attr "cc" "set_znv,compare")])
(define_insn "*condbsetreg"
[(set (match_operand:QI 0 "bit_memory_operand" "=WU")
(if_then_else:QI
(match_operator:QI 2 "eqne_operator"
[(cc0) (const_int 0)])
(ior:QI
(match_operand:QI 3 "bit_memory_operand" "0")
(ashift:QI (const_int 1)
(match_operand:QI 1 "register_operand" "r")))
(match_dup 3)))]
"TARGET_H8300SX && reload_completed"
"bset/%j2\t%R1,%0"
[(set_attr "cc" "none_0hit")
(set_attr "length_table" "logicb")])
 
(define_insn_and_split "*cmpcondbclrreg"
[(set (match_operand:QI 0 "nonimmediate_operand" "=WU,WU")
(if_then_else:QI
(match_operator
1 "eqne_operator"
[(match_operand 2 "h8300_dst_operand" "r,rQ")
(match_operand 3 "h8300_src_operand" "I,rQi")])
(and:QI
(match_operand:QI 4 "bit_memory_operand" "0,0")
(ashift:QI (const_int 1)
(match_operand:QI 5 "register_operand" "r,r")))
(match_dup 4)))]
"TARGET_H8300SX"
"#"
"reload_completed"
[(set (cc0) (match_dup 6))
(set (match_dup 0)
(if_then_else:QI
(match_op_dup 1 [(cc0) (const_int 0)])
(and:QI (match_dup 4)
(ashift:QI (const_int 1)
(match_operand:QI 5 "register_operand" "r,r")))
(match_dup 4)))]
"
{
if (operands[3] == const0_rtx && GET_CODE (operands[2]) == REG)
operands[6] = operands[2];
else
operands[6] = gen_rtx_COMPARE (VOIDmode, operands[2], operands[3]);
}"
[(set_attr "cc" "set_znv,compare")])
(define_insn "*condbclrreg"
[(set (match_operand:QI 0 "bit_memory_operand" "=WU")
(if_then_else:QI
(match_operator:QI 2 "eqne_operator"
[(cc0) (const_int 0)])
(and:QI
(match_operand:QI 3 "bit_memory_operand" "0")
(ashift:QI (const_int 1)
(match_operand:QI 1 "register_operand" "r")))
(match_dup 3)))]
"TARGET_H8300SX && reload_completed"
"bclr/%j2\t%R1,%0"
[(set_attr "cc" "none_0hit")
(set_attr "length_table" "logicb")])
 
;; -----------------------------------------------------------------
;; COMBINE PATTERNS
;; -----------------------------------------------------------------
 
;; insv:SI
 
(define_insn "*insv_si_1_n"
[(set (zero_extract:SI (match_operand:SI 0 "register_operand" "+r")
(const_int 1)
(match_operand:SI 1 "const_int_operand" "n"))
(match_operand:SI 2 "register_operand" "r"))]
"(TARGET_H8300H || TARGET_H8300S)
&& INTVAL (operands[1]) < 16"
"bld\\t#0,%w2\;bst\\t%Z1,%Y0"
[(set_attr "length" "4")])
 
(define_insn "*insv_si_1_n_lshiftrt"
[(set (zero_extract:SI (match_operand:SI 0 "register_operand" "+r")
(const_int 1)
(match_operand:SI 1 "const_int_operand" "n"))
(lshiftrt:SI (match_operand:SI 2 "register_operand" "r")
(match_operand:SI 3 "const_int_operand" "n")))]
"(TARGET_H8300H || TARGET_H8300S)
&& INTVAL (operands[1]) < 16
&& INTVAL (operands[3]) < 16"
"bld\\t%Z3,%Y2\;bst\\t%Z1,%Y0"
[(set_attr "length" "4")])
 
(define_insn "*insv_si_1_n_lshiftrt_16"
[(set (zero_extract:SI (match_operand:SI 0 "register_operand" "+r")
(const_int 1)
(match_operand:SI 1 "const_int_operand" "n"))
(lshiftrt:SI (match_operand:SI 2 "register_operand" "r")
(const_int 16)))]
"(TARGET_H8300H || TARGET_H8300S)
&& INTVAL (operands[1]) < 16"
"rotr.w\\t%e2\;rotl.w\\t%e2\;bst\\t%Z1,%Y0"
[(set_attr "length" "6")])
 
(define_insn "*insv_si_8_8"
[(set (zero_extract:SI (match_operand:SI 0 "register_operand" "+r")
(const_int 8)
(const_int 8))
(match_operand:SI 1 "register_operand" "r"))]
"TARGET_H8300H || TARGET_H8300S"
"mov.b\\t%w1,%x0"
[(set_attr "length" "2")])
 
(define_insn "*insv_si_8_8_lshiftrt_8"
[(set (zero_extract:SI (match_operand:SI 0 "register_operand" "+r")
(const_int 8)
(const_int 8))
(lshiftrt:SI (match_operand:SI 1 "register_operand" "r")
(const_int 8)))]
"TARGET_H8300H || TARGET_H8300S"
"mov.b\\t%x1,%x0"
[(set_attr "length" "2")])
 
;; extzv:SI
 
(define_insn "*extzv_8_8"
[(set (match_operand:SI 0 "register_operand" "=r,r")
(zero_extract:SI (match_operand:SI 1 "register_operand" "?0,r")
(const_int 8)
(const_int 8)))]
"TARGET_H8300H || TARGET_H8300S"
"@
mov.b\\t%x1,%w0\;extu.w\\t%f0\;extu.l\\t%S0
sub.l\\t%S0,%S0\;mov.b\\t%x1,%w0"
[(set_attr "cc" "set_znv,clobber")
(set_attr "length" "6,4")])
 
(define_insn "*extzv_8_16"
[(set (match_operand:SI 0 "register_operand" "=r")
(zero_extract:SI (match_operand:SI 1 "register_operand" "r")
(const_int 8)
(const_int 16)))]
"TARGET_H8300H || TARGET_H8300S"
"mov.w\\t%e1,%f0\;extu.w\\t%f0\;extu.l\\t%S0"
[(set_attr "cc" "set_znv")
(set_attr "length" "6")])
 
(define_insn "*extzv_16_8"
[(set (match_operand:SI 0 "register_operand" "=r")
(zero_extract:SI (match_operand:SI 1 "register_operand" "r")
(const_int 16)
(const_int 8)))
(clobber (match_scratch:SI 2 "=&r"))]
"TARGET_H8300H"
"mov.w\\t%e1,%f2\;mov.b\\t%x1,%w0\;mov.b\\t%w2,%x0\;extu.l\\t%S0"
[(set_attr "length" "8")
(set_attr "cc" "set_znv")])
 
;; Extract the exponent of a float.
 
(define_insn_and_split "*extzv_8_23"
[(set (match_operand:SI 0 "register_operand" "=r")
(zero_extract:SI (match_operand:SI 1 "register_operand" "0")
(const_int 8)
(const_int 23)))]
"(TARGET_H8300H || TARGET_H8300S)"
"#"
"&& reload_completed"
[(parallel [(set (match_dup 0)
(ashift:SI (match_dup 0)
(const_int 1)))
(clobber (scratch:QI))])
(parallel [(set (match_dup 0)
(lshiftrt:SI (match_dup 0)
(const_int 24)))
(clobber (scratch:QI))])]
"")
 
;; and:SI
 
;; ((SImode) HImode) << 15
 
(define_insn_and_split "*twoshifts_l16_r1"
[(set (match_operand:SI 0 "register_operand" "=r")
(and:SI (ashift:SI (match_operand:SI 1 "register_operand" "0")
(const_int 15))
(const_int 2147450880)))]
"(TARGET_H8300H || TARGET_H8300S)"
"#"
"&& reload_completed"
[(parallel [(set (match_dup 0)
(ashift:SI (match_dup 0)
(const_int 16)))
(clobber (scratch:QI))])
(parallel [(set (match_dup 0)
(lshiftrt:SI (match_dup 0)
(const_int 1)))
(clobber (scratch:QI))])]
"")
 
;; Transform (SImode << B) & 0xffff into (SImode) (HImode << B).
 
(define_insn_and_split "*andsi3_ashift_n_lower"
[(set (match_operand:SI 0 "register_operand" "=r,r")
(and:SI (ashift:SI (match_operand:SI 1 "register_operand" "0,0")
(match_operand:QI 2 "const_int_operand" "S,n"))
(match_operand:SI 3 "const_int_operand" "n,n")))
(clobber (match_scratch:QI 4 "=X,&r"))]
"(TARGET_H8300H || TARGET_H8300S)
&& INTVAL (operands[2]) <= 15
&& INTVAL (operands[3]) == ((-1 << INTVAL (operands[2])) & 0xffff)"
"#"
"&& reload_completed"
[(parallel [(set (match_dup 5)
(ashift:HI (match_dup 5)
(match_dup 2)))
(clobber (match_dup 4))])
(set (match_dup 0)
(zero_extend:SI (match_dup 5)))]
"operands[5] = gen_rtx_REG (HImode, REGNO (operands[0]));")
 
;; Accept (A >> 30) & 2 and the like.
 
(define_insn "*andsi3_lshiftrt_n_sb"
[(set (match_operand:SI 0 "register_operand" "=r")
(and:SI (lshiftrt:SI (match_operand:SI 1 "register_operand" "0")
(match_operand:SI 2 "const_int_operand" "n"))
(match_operand:SI 3 "single_one_operand" "n")))]
"(TARGET_H8300H || TARGET_H8300S)
&& exact_log2 (INTVAL (operands[3])) < 16
&& INTVAL (operands[2]) + exact_log2 (INTVAL (operands[3])) == 31"
"*
{
operands[3] = GEN_INT (exact_log2 (INTVAL (operands[3])));
return \"shll.l\\t%S0\;xor.l\\t%S0,%S0\;bst\\t%Z3,%Y0\";
}"
[(set_attr "length" "8")])
 
(define_insn_and_split "*andsi3_lshiftrt_9_sb"
[(set (match_operand:SI 0 "register_operand" "=r")
(and:SI (lshiftrt:SI (match_operand:SI 1 "register_operand" "0")
(const_int 9))
(const_int 4194304)))]
"(TARGET_H8300H || TARGET_H8300S)"
"#"
"&& reload_completed"
[(set (match_dup 0)
(and:SI (lshiftrt:SI (match_dup 0)
(const_int 25))
(const_int 64)))
(parallel [(set (match_dup 0)
(ashift:SI (match_dup 0)
(const_int 16)))
(clobber (scratch:QI))])]
"")
 
;; plus:SI
 
(define_insn "*addsi3_upper"
[(set (match_operand:SI 0 "register_operand" "=r")
(plus:SI (mult:SI (match_operand:SI 1 "register_operand" "r")
(const_int 65536))
(match_operand:SI 2 "register_operand" "0")))]
"TARGET_H8300H || TARGET_H8300S"
"add.w\\t%f1,%e0"
[(set_attr "length" "2")])
 
(define_insn "*addsi3_lshiftrt_16_zexthi"
[(set (match_operand:SI 0 "register_operand" "=r")
(plus:SI (lshiftrt:SI (match_operand:SI 1 "register_operand" "r")
(const_int 16))
(zero_extend:SI (match_operand:HI 2 "register_operand" "0"))))]
"TARGET_H8300H || TARGET_H8300S"
"add.w\\t%e1,%f0\;xor.w\\t%e0,%e0\;rotxl.w\\t%e0"
[(set_attr "length" "6")])
 
(define_insn_and_split "*addsi3_and_r_1"
[(set (match_operand:SI 0 "register_operand" "=r")
(plus:SI (and:SI (match_operand:SI 1 "register_operand" "r")
(const_int 1))
(match_operand:SI 2 "register_operand" "0")))]
"(TARGET_H8300H || TARGET_H8300S)"
"#"
"&& reload_completed"
[(set (cc0)
(zero_extract:SI (match_dup 1)
(const_int 1)
(const_int 0)))
(set (pc)
(if_then_else (eq (cc0)
(const_int 0))
(label_ref (match_dup 3))
(pc)))
(set (match_dup 2)
(plus:SI (match_dup 2)
(const_int 1)))
(match_dup 3)]
"operands[3] = gen_label_rtx ();")
 
(define_insn_and_split "*addsi3_and_not_r_1"
[(set (match_operand:SI 0 "register_operand" "=r")
(plus:SI (and:SI (not:SI (match_operand:SI 1 "register_operand" "r"))
(const_int 1))
(match_operand:SI 2 "register_operand" "0")))]
"(TARGET_H8300H || TARGET_H8300S)"
"#"
"&& reload_completed"
[(set (cc0)
(zero_extract:SI (match_dup 1)
(const_int 1)
(const_int 0)))
(set (pc)
(if_then_else (ne (cc0)
(const_int 0))
(label_ref (match_dup 3))
(pc)))
(set (match_dup 2)
(plus:SI (match_dup 2)
(const_int 1)))
(match_dup 3)]
"operands[3] = gen_label_rtx ();")
 
;; [ix]or:HI
 
(define_insn "*ixorhi3_zext"
[(set (match_operand:HI 0 "register_operand" "=r")
(match_operator:HI 1 "iorxor_operator"
[(zero_extend:HI (match_operand:QI 2 "register_operand" "r"))
(match_operand:HI 3 "register_operand" "0")]))]
""
"%c1.b\\t%X2,%s0"
[(set_attr "length" "2")])
 
;; [ix]or:SI
 
(define_insn "*ixorsi3_zext_qi"
[(set (match_operand:SI 0 "register_operand" "=r")
(match_operator:SI 1 "iorxor_operator"
[(zero_extend:SI (match_operand:QI 2 "register_operand" "r"))
(match_operand:SI 3 "register_operand" "0")]))]
""
"%c1.b\\t%X2,%w0"
[(set_attr "length" "2")])
 
(define_insn "*ixorsi3_zext_hi"
[(set (match_operand:SI 0 "register_operand" "=r")
(match_operator:SI 1 "iorxor_operator"
[(zero_extend:SI (match_operand:HI 2 "register_operand" "r"))
(match_operand:SI 3 "register_operand" "0")]))]
"TARGET_H8300H || TARGET_H8300S"
"%c1.w\\t%T2,%f0"
[(set_attr "length" "2")])
 
(define_insn "*ixorsi3_ashift_16"
[(set (match_operand:SI 0 "register_operand" "=r")
(match_operator:SI 1 "iorxor_operator"
[(ashift:SI (match_operand:SI 2 "register_operand" "r")
(const_int 16))
(match_operand:SI 3 "register_operand" "0")]))]
"TARGET_H8300H || TARGET_H8300S"
"%c1.w\\t%f2,%e0"
[(set_attr "length" "2")])
 
(define_insn "*ixorsi3_lshiftrt_16"
[(set (match_operand:SI 0 "register_operand" "=r")
(match_operator:SI 1 "iorxor_operator"
[(lshiftrt:SI (match_operand:SI 2 "register_operand" "r")
(const_int 16))
(match_operand:SI 3 "register_operand" "0")]))]
"TARGET_H8300H || TARGET_H8300S"
"%c1.w\\t%e2,%f0"
[(set_attr "length" "2")])
 
;; ior:HI
 
(define_insn "*iorhi3_ashift_8"
[(set (match_operand:HI 0 "register_operand" "=r")
(ior:HI (ashift:HI (match_operand:HI 1 "register_operand" "r")
(const_int 8))
(match_operand:HI 2 "register_operand" "0")))]
""
"or.b\\t%s1,%t0"
[(set_attr "length" "2")])
 
(define_insn "*iorhi3_lshiftrt_8"
[(set (match_operand:HI 0 "register_operand" "=r")
(ior:HI (lshiftrt:HI (match_operand:HI 1 "register_operand" "r")
(const_int 8))
(match_operand:HI 2 "register_operand" "0")))]
""
"or.b\\t%t1,%s0"
[(set_attr "length" "2")])
 
(define_insn "*iorhi3_two_qi"
[(set (match_operand:HI 0 "register_operand" "=r")
(ior:HI (zero_extend:HI (match_operand:QI 1 "register_operand" "0"))
(ashift:HI (match_operand:HI 2 "register_operand" "r")
(const_int 8))))]
""
"mov.b\\t%s2,%t0"
[(set_attr "length" "2")])
 
(define_insn "*iorhi3_two_qi_mem"
[(set (match_operand:HI 0 "register_operand" "=&r")
(ior:HI (zero_extend:HI (match_operand:QI 1 "memory_operand" "m"))
(ashift:HI (subreg:HI (match_operand:QI 2 "memory_operand" "m") 0)
(const_int 8))))]
""
"mov.b\\t%X2,%t0\;mov.b\\t%X1,%s0"
[(set_attr "length" "16")])
 
(define_split
[(set (match_operand:HI 0 "register_operand" "")
(ior:HI (zero_extend:HI (match_operand:QI 1 "memory_operand" ""))
(ashift:HI (subreg:HI (match_operand:QI 2 "memory_operand" "") 0)
(const_int 8))))]
"(TARGET_H8300H || TARGET_H8300S)
&& reload_completed
&& byte_accesses_mergeable_p (XEXP (operands[2], 0), XEXP (operands[1], 0))"
[(set (match_dup 0)
(match_dup 3))]
"operands[3] = gen_rtx_MEM (HImode, XEXP (operands[2], 0));")
 
;; ior:SI
 
(define_insn "*iorsi3_two_hi"
[(set (match_operand:SI 0 "register_operand" "=r")
(ior:SI (zero_extend:SI (match_operand:HI 1 "register_operand" "0"))
(ashift:SI (match_operand:SI 2 "register_operand" "r")
(const_int 16))))]
"TARGET_H8300H || TARGET_H8300S"
"mov.w\\t%f2,%e0"
[(set_attr "length" "2")])
 
(define_insn_and_split "*iorsi3_two_qi_zext"
[(set (match_operand:SI 0 "register_operand" "=&r")
(ior:SI (zero_extend:SI (match_operand:QI 1 "memory_operand" "m"))
 
(and:SI (ashift:SI (subreg:SI (match_operand:QI 2 "memory_operand" "m") 0)
(const_int 8))
(const_int 65280))))]
"(TARGET_H8300H || TARGET_H8300S)"
"#"
"&& reload_completed"
[(set (match_dup 3)
(ior:HI (zero_extend:HI (match_dup 1))
(ashift:HI (subreg:HI (match_dup 2) 0)
(const_int 8))))
(set (match_dup 0)
(zero_extend:SI (match_dup 3)))]
"operands[3] = gen_rtx_REG (HImode, REGNO (operands[0]));")
 
(define_insn "*iorsi3_e2f"
[(set (match_operand:SI 0 "register_operand" "=r")
(ior:SI (and:SI (match_operand:SI 1 "register_operand" "0")
(const_int -65536))
(lshiftrt:SI (match_operand:SI 2 "register_operand" "r")
(const_int 16))))]
"TARGET_H8300H || TARGET_H8300S"
"mov.w\\t%e2,%f0"
[(set_attr "length" "2")])
 
(define_insn_and_split "*iorsi3_two_qi_sext"
[(set (match_operand:SI 0 "register_operand" "=r")
(ior:SI (zero_extend:SI (match_operand:QI 1 "register_operand" "0"))
(ashift:SI (sign_extend:SI (match_operand:QI 2 "register_operand" "r"))
(const_int 8))))]
"(TARGET_H8300H || TARGET_H8300S)"
"#"
"&& reload_completed"
[(set (match_dup 3)
(ior:HI (zero_extend:HI (match_dup 1))
(ashift:HI (match_dup 4)
(const_int 8))))
(set (match_dup 0)
(sign_extend:SI (match_dup 3)))]
"operands[3] = gen_rtx_REG (HImode, REGNO (operands[0]));
operands[4] = gen_rtx_REG (HImode, REGNO (operands[2]));")
 
(define_insn "*iorsi3_w"
[(set (match_operand:SI 0 "register_operand" "=r,&r")
(ior:SI (and:SI (match_operand:SI 1 "register_operand" "0,0")
(const_int -256))
(zero_extend:SI (match_operand:QI 2 "general_operand_src" "r,g>"))))]
"TARGET_H8300H || TARGET_H8300S"
"mov.b\\t%X2,%w0"
[(set_attr "length" "2,8")])
 
(define_insn "*iorsi3_ashift_31"
[(set (match_operand:SI 0 "register_operand" "=&r")
(ior:SI (ashift:SI (match_operand:SI 1 "register_operand" "r")
(const_int 31))
(match_operand:SI 2 "register_operand" "0")))]
"TARGET_H8300H || TARGET_H8300S"
"rotxl.l\\t%S0\;bor\\t#0,%w1\;rotxr.l\\t%S0"
[(set_attr "length" "6")
(set_attr "cc" "set_znv")])
 
(define_insn "*iorsi3_and_ashift"
[(set (match_operand:SI 0 "register_operand" "=r")
(ior:SI (and:SI (ashift:SI (match_operand:SI 1 "register_operand" "r")
(match_operand:SI 2 "const_int_operand" "n"))
(match_operand:SI 3 "single_one_operand" "n"))
(match_operand:SI 4 "register_operand" "0")))]
"(TARGET_H8300H || TARGET_H8300S)
&& (INTVAL (operands[3]) & ~0xffff) == 0"
"*
{
rtx srcpos = GEN_INT (exact_log2 (INTVAL (operands[3]))
- INTVAL (operands[2]));
rtx dstpos = GEN_INT (exact_log2 (INTVAL (operands[3])));
operands[2] = srcpos;
operands[3] = dstpos;
return \"bld\\t%Z2,%Y1\;bor\\t%Z3,%Y0\;bst\\t%Z3,%Y0\";
}"
[(set_attr "length" "6")])
 
(define_insn "*iorsi3_and_lshiftrt"
[(set (match_operand:SI 0 "register_operand" "=r")
(ior:SI (and:SI (lshiftrt:SI (match_operand:SI 1 "register_operand" "r")
(match_operand:SI 2 "const_int_operand" "n"))
(match_operand:SI 3 "single_one_operand" "n"))
(match_operand:SI 4 "register_operand" "0")))]
"(TARGET_H8300H || TARGET_H8300S)
&& ((INTVAL (operands[3]) << INTVAL (operands[2])) & ~0xffff) == 0"
"*
{
rtx srcpos = GEN_INT (exact_log2 (INTVAL (operands[3]))
+ INTVAL (operands[2]));
rtx dstpos = GEN_INT (exact_log2 (INTVAL (operands[3])));
operands[2] = srcpos;
operands[3] = dstpos;
return \"bld\\t%Z2,%Y1\;bor\\t%Z3,%Y0\;bst\\t%Z3,%Y0\";
}"
[(set_attr "length" "6")])
 
(define_insn "*iorsi3_zero_extract"
[(set (match_operand:SI 0 "register_operand" "=r")
(ior:SI (zero_extract:SI (match_operand:SI 1 "register_operand" "r")
(const_int 1)
(match_operand:SI 2 "const_int_operand" "n"))
(match_operand:SI 3 "register_operand" "0")))]
"(TARGET_H8300H || TARGET_H8300S)
&& INTVAL (operands[2]) < 16"
"bld\\t%Z2,%Y1\;bor\\t#0,%w0\;bst\\t#0,%w0"
[(set_attr "length" "6")])
 
(define_insn "*iorsi3_and_lshiftrt_n_sb"
[(set (match_operand:SI 0 "register_operand" "=r")
(ior:SI (and:SI (lshiftrt:SI (match_operand:SI 1 "register_operand" "r")
(const_int 30))
(const_int 2))
(match_operand:SI 2 "register_operand" "0")))]
"(TARGET_H8300H || TARGET_H8300S)"
"rotl.l\\t%S1\;rotr.l\\t%S1\;bor\\t#1,%w0\;bst\\t#1,%w0"
[(set_attr "length" "8")])
 
(define_insn "*iorsi3_and_lshiftrt_9_sb"
[(set (match_operand:SI 0 "register_operand" "=r")
(ior:SI (and:SI (lshiftrt:SI (match_operand:SI 1 "register_operand" "r")
(const_int 9))
(const_int 4194304))
(match_operand:SI 2 "register_operand" "0")))
(clobber (match_scratch:HI 3 "=&r"))]
"(TARGET_H8300H || TARGET_H8300S)"
"*
{
if (find_regno_note (insn, REG_DEAD, REGNO (operands[1])))
return \"shll.l\\t%S1\;xor.w\\t%T3,%T3\;bst\\t#6,%s3\;or.w\\t%T3,%e0\";
else
return \"rotl.l\\t%S1\;rotr.l\\t%S1\;xor.w\\t%T3,%T3\;bst\\t#6,%s3\;or.w\\t%T3,%e0\";
}"
[(set_attr "length" "10")])
 
;; Used to OR the exponent of a float.
 
(define_insn "*iorsi3_shift"
[(set (match_operand:SI 0 "register_operand" "=r")
(ior:SI (ashift:SI (match_operand:SI 1 "register_operand" "r")
(const_int 23))
(match_operand:SI 2 "register_operand" "0")))
(clobber (match_scratch:SI 3 "=&r"))]
"TARGET_H8300H || TARGET_H8300S"
"#")
 
(define_split
[(set (match_operand:SI 0 "register_operand" "")
(ior:SI (ashift:SI (match_operand:SI 1 "register_operand" "")
(const_int 23))
(match_dup 0)))
(clobber (match_operand:SI 2 "register_operand" ""))]
"(TARGET_H8300H || TARGET_H8300S)
&& flow2_completed
&& find_regno_note (insn, REG_DEAD, REGNO (operands[1]))
&& REGNO (operands[0]) != REGNO (operands[1])"
[(parallel [(set (match_dup 3)
(ashift:HI (match_dup 3)
(const_int 7)))
(clobber (scratch:QI))])
(set (match_dup 0)
(ior:SI (ashift:SI (match_dup 1)
(const_int 16))
(match_dup 0)))]
"operands[3] = gen_rtx_REG (HImode, REGNO (operands[1]));")
 
(define_split
[(set (match_operand:SI 0 "register_operand" "")
(ior:SI (ashift:SI (match_operand:SI 1 "register_operand" "")
(const_int 23))
(match_dup 0)))
(clobber (match_operand:SI 2 "register_operand" ""))]
"(TARGET_H8300H || TARGET_H8300S)
&& flow2_completed
&& !(find_regno_note (insn, REG_DEAD, REGNO (operands[1]))
&& REGNO (operands[0]) != REGNO (operands[1]))"
[(set (match_dup 2)
(match_dup 1))
(parallel [(set (match_dup 3)
(ashift:HI (match_dup 3)
(const_int 7)))
(clobber (scratch:QI))])
(set (match_dup 0)
(ior:SI (ashift:SI (match_dup 2)
(const_int 16))
(match_dup 0)))]
"operands[3] = gen_rtx_REG (HImode, REGNO (operands[2]));")
 
(define_insn "*iorsi2_and_1_lshiftrt_1"
[(set (match_operand:SI 0 "register_operand" "=r")
(ior:SI (and:SI (match_operand:SI 1 "register_operand" "0")
(const_int 1))
(lshiftrt:SI (match_dup 1)
(const_int 1))))]
"TARGET_H8300H || TARGET_H8300S"
"shlr.l\\t%S0\;bor\\t#0,%w0\;bst\\t#0,%w0"
[(set_attr "length" "6")])
 
(define_insn_and_split "*iorsi3_ashift_16_ashift_24"
[(set (match_operand:SI 0 "register_operand" "=r")
(ior:SI (ashift:SI (match_operand:SI 1 "register_operand" "0")
(const_int 16))
(ashift:SI (match_operand:SI 2 "register_operand" "r")
(const_int 24))))]
"(TARGET_H8300H || TARGET_H8300S)"
"#"
"&& reload_completed"
[(set (match_dup 3)
(ior:HI (ashift:HI (match_dup 4)
(const_int 8))
(match_dup 3)))
(parallel [(set (match_dup 0)
(ashift:SI (match_dup 0)
(const_int 16)))
(clobber (scratch:QI))])]
"operands[3] = gen_rtx_REG (HImode, REGNO (operands[0]));
operands[4] = gen_rtx_REG (HImode, REGNO (operands[2]));")
 
(define_insn_and_split "*iorsi3_ashift_16_ashift_24_mem"
[(set (match_operand:SI 0 "register_operand" "=&r")
(ior:SI (and:SI (ashift:SI (subreg:SI (match_operand:QI 1 "memory_operand" "m") 0)
(const_int 16))
(const_int 16711680))
(ashift:SI (subreg:SI (match_operand:QI 2 "memory_operand" "m") 0)
(const_int 24))))]
"(TARGET_H8300H || TARGET_H8300S)"
"#"
"&& reload_completed"
[(set (match_dup 3)
(ior:HI (zero_extend:HI (match_dup 1))
(ashift:HI (subreg:HI (match_dup 2) 0)
(const_int 8))))
(parallel [(set (match_dup 0)
(ashift:SI (match_dup 0)
(const_int 16)))
(clobber (scratch:QI))])]
"operands[3] = gen_rtx_REG (HImode, REGNO (operands[0]));")
 
;; Used to add the exponent of a float.
 
(define_insn "*addsi3_shift"
[(set (match_operand:SI 0 "register_operand" "=r")
(plus:SI (mult:SI (match_operand:SI 1 "register_operand" "r")
(const_int 8388608))
(match_operand:SI 2 "register_operand" "0")))
(clobber (match_scratch:SI 3 "=&r"))]
"TARGET_H8300H || TARGET_H8300S"
"#")
 
(define_split
[(set (match_operand:SI 0 "register_operand" "")
(plus:SI (mult:SI (match_operand:SI 1 "register_operand" "")
(const_int 8388608))
(match_dup 0)))
(clobber (match_operand:SI 2 "register_operand" ""))]
"(TARGET_H8300H || TARGET_H8300S)
&& flow2_completed
&& find_regno_note (insn, REG_DEAD, REGNO (operands[1]))
&& REGNO (operands[0]) != REGNO (operands[1])"
[(parallel [(set (match_dup 3)
(ashift:HI (match_dup 3)
(const_int 7)))
(clobber (scratch:QI))])
(set (match_dup 0)
(plus:SI (mult:SI (match_dup 1)
(const_int 65536))
(match_dup 0)))]
"operands[3] = gen_rtx_REG (HImode, REGNO (operands[1]));")
 
(define_split
[(set (match_operand:SI 0 "register_operand" "")
(plus:SI (mult:SI (match_operand:SI 1 "register_operand" "")
(const_int 8388608))
(match_dup 0)))
(clobber (match_operand:SI 2 "register_operand" ""))]
"(TARGET_H8300H || TARGET_H8300S)
&& flow2_completed
&& !(find_regno_note (insn, REG_DEAD, REGNO (operands[1]))
&& REGNO (operands[0]) != REGNO (operands[1]))"
[(set (match_dup 2)
(match_dup 1))
(parallel [(set (match_dup 3)
(ashift:HI (match_dup 3)
(const_int 7)))
(clobber (scratch:QI))])
(set (match_dup 0)
(plus:SI (mult:SI (match_dup 2)
(const_int 65536))
(match_dup 0)))]
"operands[3] = gen_rtx_REG (HImode, REGNO (operands[2]));")
 
;; ashift:SI
 
(define_insn_and_split "*ashiftsi_sextqi_7"
[(set (match_operand:SI 0 "register_operand" "=r")
(ashift:SI (sign_extend:SI (match_operand:QI 1 "register_operand" "0"))
(const_int 7)))]
"(TARGET_H8300H || TARGET_H8300S)"
"#"
"&& reload_completed"
[(parallel [(set (match_dup 2)
(ashift:HI (match_dup 2)
(const_int 8)))
(clobber (scratch:QI))])
(set (match_dup 0)
(sign_extend:SI (match_dup 2)))
(parallel [(set (match_dup 0)
(ashiftrt:SI (match_dup 0)
(const_int 1)))
(clobber (scratch:QI))])]
"operands[2] = gen_rtx_REG (HImode, REGNO (operands[0]));")
 
;; Storing a part of HImode to QImode.
 
(define_insn ""
[(set (match_operand:QI 0 "general_operand_dst" "=rm<")
(subreg:QI (lshiftrt:HI (match_operand:HI 1 "register_operand" "r")
(const_int 8)) 1))]
""
"mov.b\\t%t1,%R0"
[(set_attr "cc" "set_znv")
(set_attr "length" "8")])
 
;; Storing a part of SImode to QImode.
 
(define_insn ""
[(set (match_operand:QI 0 "general_operand_dst" "=rm<")
(subreg:QI (lshiftrt:SI (match_operand:SI 1 "register_operand" "r")
(const_int 8)) 3))]
""
"mov.b\\t%x1,%R0"
[(set_attr "cc" "set_znv")
(set_attr "length" "8")])
 
(define_insn ""
[(set (match_operand:QI 0 "general_operand_dst" "=rm<")
(subreg:QI (lshiftrt:SI (match_operand:SI 1 "register_operand" "r")
(const_int 16)) 3))
(clobber (match_scratch:SI 2 "=&r"))]
"TARGET_H8300H || TARGET_H8300S"
"mov.w\\t%e1,%f2\;mov.b\\t%w2,%R0"
[(set_attr "cc" "set_znv")
(set_attr "length" "10")])
 
(define_insn ""
[(set (match_operand:QI 0 "general_operand_dst" "=rm<")
(subreg:QI (lshiftrt:SI (match_operand:SI 1 "register_operand" "r")
(const_int 24)) 3))
(clobber (match_scratch:SI 2 "=&r"))]
"TARGET_H8300H || TARGET_H8300S"
"mov.w\\t%e1,%f2\;mov.b\\t%x2,%R0"
[(set_attr "cc" "set_znv")
(set_attr "length" "10")])
 
(define_insn_and_split ""
[(set (pc)
(if_then_else (eq (zero_extract:SI (subreg:SI (match_operand:QI 0 "register_operand" "") 0)
(const_int 1)
(const_int 7))
(const_int 0))
(label_ref (match_operand 1 "" ""))
(pc)))]
""
"#"
""
[(set (cc0)
(match_dup 0))
(set (pc)
(if_then_else (ge (cc0)
(const_int 0))
(label_ref (match_dup 1))
(pc)))]
"")
 
(define_insn_and_split ""
[(set (pc)
(if_then_else (ne (zero_extract:SI (subreg:SI (match_operand:QI 0 "register_operand" "") 0)
(const_int 1)
(const_int 7))
(const_int 0))
(label_ref (match_operand 1 "" ""))
(pc)))]
""
"#"
""
[(set (cc0)
(match_dup 0))
(set (pc)
(if_then_else (lt (cc0)
(const_int 0))
(label_ref (match_dup 1))
(pc)))]
"")
;; -----------------------------------------------------------------
;; PEEPHOLE PATTERNS
;; -----------------------------------------------------------------
 
;; Convert (A >> B) & C to (A & 255) >> B if C == 255 >> B.
 
(define_peephole2
[(parallel [(set (match_operand:HI 0 "register_operand" "")
(lshiftrt:HI (match_dup 0)
(match_operand:HI 1 "const_int_operand" "")))
(clobber (match_operand:HI 2 "" ""))])
(set (match_dup 0)
(and:HI (match_dup 0)
(match_operand:HI 3 "const_int_operand" "")))]
"INTVAL (operands[3]) == (255 >> INTVAL (operands[1]))"
[(set (match_dup 0)
(and:HI (match_dup 0)
(const_int 255)))
(parallel
[(set (match_dup 0)
(lshiftrt:HI (match_dup 0)
(match_dup 1)))
(clobber (match_dup 2))])]
"")
 
;; Convert (A << B) & C to (A & 255) << B if C == 255 << B.
 
(define_peephole2
[(parallel [(set (match_operand:HI 0 "register_operand" "")
(ashift:HI (match_dup 0)
(match_operand:HI 1 "const_int_operand" "")))
(clobber (match_operand:HI 2 "" ""))])
(set (match_dup 0)
(and:HI (match_dup 0)
(match_operand:HI 3 "const_int_operand" "")))]
"INTVAL (operands[3]) == (255 << INTVAL (operands[1]))"
[(set (match_dup 0)
(and:HI (match_dup 0)
(const_int 255)))
(parallel
[(set (match_dup 0)
(ashift:HI (match_dup 0)
(match_dup 1)))
(clobber (match_dup 2))])]
"")
 
;; Convert (A >> B) & C to (A & 255) >> B if C == 255 >> B.
 
(define_peephole2
[(parallel [(set (match_operand:SI 0 "register_operand" "")
(lshiftrt:SI (match_dup 0)
(match_operand:SI 1 "const_int_operand" "")))
(clobber (match_operand:SI 2 "" ""))])
(set (match_dup 0)
(and:SI (match_dup 0)
(match_operand:SI 3 "const_int_operand" "")))]
"INTVAL (operands[3]) == (255 >> INTVAL (operands[1]))"
[(set (match_dup 0)
(and:SI (match_dup 0)
(const_int 255)))
(parallel
[(set (match_dup 0)
(lshiftrt:SI (match_dup 0)
(match_dup 1)))
(clobber (match_dup 2))])]
"")
 
;; Convert (A << B) & C to (A & 255) << B if C == 255 << B.
 
(define_peephole2
[(parallel [(set (match_operand:SI 0 "register_operand" "")
(ashift:SI (match_dup 0)
(match_operand:SI 1 "const_int_operand" "")))
(clobber (match_operand:SI 2 "" ""))])
(set (match_dup 0)
(and:SI (match_dup 0)
(match_operand:SI 3 "const_int_operand" "")))]
"INTVAL (operands[3]) == (255 << INTVAL (operands[1]))"
[(set (match_dup 0)
(and:SI (match_dup 0)
(const_int 255)))
(parallel
[(set (match_dup 0)
(ashift:SI (match_dup 0)
(match_dup 1)))
(clobber (match_dup 2))])]
"")
 
;; Convert (A >> B) & C to (A & 65535) >> B if C == 65535 >> B.
 
(define_peephole2
[(parallel [(set (match_operand:SI 0 "register_operand" "")
(lshiftrt:SI (match_dup 0)
(match_operand:SI 1 "const_int_operand" "")))
(clobber (match_operand:SI 2 "" ""))])
(set (match_dup 0)
(and:SI (match_dup 0)
(match_operand:SI 3 "const_int_operand" "")))]
"INTVAL (operands[3]) == (65535 >> INTVAL (operands[1]))"
[(set (match_dup 0)
(and:SI (match_dup 0)
(const_int 65535)))
(parallel
[(set (match_dup 0)
(lshiftrt:SI (match_dup 0)
(match_dup 1)))
(clobber (match_dup 2))])]
"")
 
;; Convert (A << B) & C to (A & 65535) << B if C == 65535 << B.
 
(define_peephole2
[(parallel [(set (match_operand:SI 0 "register_operand" "")
(ashift:SI (match_dup 0)
(match_operand:SI 1 "const_int_operand" "")))
(clobber (match_operand:SI 2 "" ""))])
(set (match_dup 0)
(and:SI (match_dup 0)
(match_operand:SI 3 "const_int_operand" "")))]
"INTVAL (operands[3]) == (65535 << INTVAL (operands[1]))"
[(set (match_dup 0)
(and:SI (match_dup 0)
(const_int 65535)))
(parallel
[(set (match_dup 0)
(ashift:SI (match_dup 0)
(match_dup 1)))
(clobber (match_dup 2))])]
"")
 
;; Convert a QImode push into an SImode push so that the
;; define_peephole2 below can cram multiple pushes into one stm.l.
 
(define_peephole2
[(parallel [(set (reg:SI SP_REG)
(plus:SI (reg:SI SP_REG) (const_int -4)))
(set (mem:QI (plus:SI (reg:SI SP_REG) (const_int -3)))
(match_operand:QI 0 "register_operand" ""))])]
"TARGET_H8300S && !TARGET_NORMAL_MODE && REGNO (operands[0]) != SP_REG"
[(set (mem:SI (pre_dec:SI (reg:SI SP_REG)))
(match_dup 0))]
"operands[0] = gen_rtx_REG (SImode, REGNO (operands[0]));")
 
(define_peephole2
[(parallel [(set (reg:HI SP_REG)
(plus:HI (reg:HI SP_REG) (const_int -4)))
(set (mem:QI (plus:HI (reg:HI SP_REG) (const_int -3)))
(match_operand:QI 0 "register_operand" ""))])]
"TARGET_H8300S && TARGET_NORMAL_MODE && REGNO (operands[0]) != SP_REG"
[(set (mem:SI (pre_dec:HI (reg:HI SP_REG)))
(match_dup 0))]
"operands[0] = gen_rtx_REG (SImode, REGNO (operands[0]));")
 
;; Convert a HImode push into an SImode push so that the
;; define_peephole2 below can cram multiple pushes into one stm.l.
 
(define_peephole2
[(parallel [(set (reg:SI SP_REG)
(plus:SI (reg:SI SP_REG) (const_int -4)))
(set (mem:HI (plus:SI (reg:SI SP_REG) (const_int -2)))
(match_operand:HI 0 "register_operand" ""))])]
"TARGET_H8300S && !TARGET_NORMAL_MODE && REGNO (operands[0]) != SP_REG"
[(set (mem:SI (pre_dec:SI (reg:SI SP_REG)))
(match_dup 0))]
"operands[0] = gen_rtx_REG (SImode, REGNO (operands[0]));")
 
(define_peephole2
[(parallel [(set (reg:HI SP_REG)
(plus:HI (reg:HI SP_REG) (const_int -4)))
(set (mem:HI (plus:HI (reg:HI SP_REG) (const_int -2)))
(match_operand:HI 0 "register_operand" ""))])]
"TARGET_H8300S && TARGET_NORMAL_MODE && REGNO (operands[0]) != SP_REG"
[(set (mem:SI (pre_dec:HI (reg:HI SP_REG)))
(match_dup 0))]
"operands[0] = gen_rtx_REG (SImode, REGNO (operands[0]));")
 
;; Cram four pushes into stm.l.
 
(define_peephole2
[(set (mem:SI (pre_dec:SI (reg:SI SP_REG)))
(match_operand:SI 0 "register_operand" ""))
(set (mem:SI (pre_dec:SI (reg:SI SP_REG)))
(match_operand:SI 1 "register_operand" ""))
(set (mem:SI (pre_dec:SI (reg:SI SP_REG)))
(match_operand:SI 2 "register_operand" ""))
(set (mem:SI (pre_dec:SI (reg:SI SP_REG)))
(match_operand:SI 3 "register_operand" ""))]
"TARGET_H8300S && !TARGET_NORMAL_MODE
&& (REGNO_REG_CLASS (REGNO (operands[3])) == GENERAL_REGS
&& REGNO (operands[1]) == REGNO (operands[0]) + 1
&& REGNO (operands[2]) == REGNO (operands[0]) + 2
&& REGNO (operands[3]) == REGNO (operands[0]) + 3
&& (TARGET_H8300SX || REGNO (operands[0]) == 0))"
[(parallel [(set (mem:SI (plus:SI (reg:SI SP_REG) (const_int -4)))
(match_dup 0))
(set (mem:SI (plus:SI (reg:SI SP_REG) (const_int -8)))
(match_dup 1))
(set (mem:SI (plus:SI (reg:SI SP_REG) (const_int -12)))
(match_dup 2))
(set (mem:SI (plus:SI (reg:SI SP_REG) (const_int -16)))
(match_dup 3))
(set (reg:SI SP_REG)
(plus:SI (reg:SI SP_REG)
(const_int -16)))])]
"")
 
(define_peephole2
[(set (mem:SI (pre_dec:HI (reg:HI SP_REG)))
(match_operand:SI 0 "register_operand" ""))
(set (mem:SI (pre_dec:HI (reg:HI SP_REG)))
(match_operand:SI 1 "register_operand" ""))
(set (mem:SI (pre_dec:HI (reg:HI SP_REG)))
(match_operand:SI 2 "register_operand" ""))
(set (mem:SI (pre_dec:HI (reg:HI SP_REG)))
(match_operand:SI 3 "register_operand" ""))]
"TARGET_H8300S && TARGET_NORMAL_MODE
&& (REGNO_REG_CLASS (REGNO (operands[3])) == GENERAL_REGS
&& REGNO (operands[1]) == REGNO (operands[0]) + 1
&& REGNO (operands[2]) == REGNO (operands[0]) + 2
&& REGNO (operands[3]) == REGNO (operands[0]) + 3
&& (TARGET_H8300SX || REGNO (operands[0]) == 0))"
[(parallel [(set (mem:SI (plus:HI (reg:HI SP_REG) (const_int -4)))
(match_dup 0))
(set (mem:SI (plus:HI (reg:HI SP_REG) (const_int -8)))
(match_dup 1))
(set (mem:SI (plus:HI (reg:HI SP_REG) (const_int -12)))
(match_dup 2))
(set (mem:SI (plus:HI (reg:HI SP_REG) (const_int -16)))
(match_dup 3))
(set (reg:HI SP_REG)
(plus:HI (reg:HI SP_REG)
(const_int -16)))])]
"")
 
;; Cram three pushes into stm.l.
 
(define_peephole2
[(set (mem:SI (pre_dec:SI (reg:SI SP_REG)))
(match_operand:SI 0 "register_operand" ""))
(set (mem:SI (pre_dec:SI (reg:SI SP_REG)))
(match_operand:SI 1 "register_operand" ""))
(set (mem:SI (pre_dec:SI (reg:SI SP_REG)))
(match_operand:SI 2 "register_operand" ""))]
"TARGET_H8300S && !TARGET_NORMAL_MODE
&& (REGNO_REG_CLASS (REGNO (operands[2])) == GENERAL_REGS
&& REGNO (operands[1]) == REGNO (operands[0]) + 1
&& REGNO (operands[2]) == REGNO (operands[0]) + 2
&& (TARGET_H8300SX || (REGNO (operands[0]) & 3) == 0))"
[(parallel [(set (mem:SI (plus:SI (reg:SI SP_REG) (const_int -4)))
(match_dup 0))
(set (mem:SI (plus:SI (reg:SI SP_REG) (const_int -8)))
(match_dup 1))
(set (mem:SI (plus:SI (reg:SI SP_REG) (const_int -12)))
(match_dup 2))
(set (reg:SI SP_REG)
(plus:SI (reg:SI SP_REG)
(const_int -12)))])]
"")
 
(define_peephole2
[(set (mem:SI (pre_dec:HI (reg:HI SP_REG)))
(match_operand:SI 0 "register_operand" ""))
(set (mem:SI (pre_dec:HI (reg:HI SP_REG)))
(match_operand:SI 1 "register_operand" ""))
(set (mem:SI (pre_dec:HI (reg:HI SP_REG)))
(match_operand:SI 2 "register_operand" ""))]
"TARGET_H8300S && TARGET_NORMAL_MODE
&& (REGNO_REG_CLASS (REGNO (operands[2])) == GENERAL_REGS
&& REGNO (operands[1]) == REGNO (operands[0]) + 1
&& REGNO (operands[2]) == REGNO (operands[0]) + 2
&& (TARGET_H8300SX || (REGNO (operands[0]) & 3) == 0))"
[(parallel [(set (mem:SI (plus:HI (reg:HI SP_REG) (const_int -4)))
(match_dup 0))
(set (mem:SI (plus:HI (reg:HI SP_REG) (const_int -8)))
(match_dup 1))
(set (mem:SI (plus:HI (reg:HI SP_REG) (const_int -12)))
(match_dup 2))
(set (reg:HI SP_REG)
(plus:HI (reg:HI SP_REG)
(const_int -12)))])]
"")
 
;; Cram two pushes into stm.l.
 
(define_peephole2
[(set (mem:SI (pre_dec:SI (reg:SI SP_REG)))
(match_operand:SI 0 "register_operand" ""))
(set (mem:SI (pre_dec:SI (reg:SI SP_REG)))
(match_operand:SI 1 "register_operand" ""))]
"TARGET_H8300S && !TARGET_NORMAL_MODE
&& (REGNO_REG_CLASS (REGNO (operands[1])) == GENERAL_REGS
&& REGNO (operands[1]) == REGNO (operands[0]) + 1
&& (TARGET_H8300SX || (REGNO (operands[0]) & 1) == 0))"
[(parallel [(set (mem:SI (plus:SI (reg:SI SP_REG) (const_int -4)))
(match_dup 0))
(set (mem:SI (plus:SI (reg:SI SP_REG) (const_int -8)))
(match_dup 1))
(set (reg:SI SP_REG)
(plus:SI (reg:SI SP_REG)
(const_int -8)))])]
"")
 
(define_peephole2
[(set (mem:SI (pre_dec:HI (reg:HI SP_REG)))
(match_operand:SI 0 "register_operand" ""))
(set (mem:SI (pre_dec:HI (reg:HI SP_REG)))
(match_operand:SI 1 "register_operand" ""))]
"TARGET_H8300S && TARGET_NORMAL_MODE
&& (REGNO_REG_CLASS (REGNO (operands[1])) == GENERAL_REGS
&& REGNO (operands[1]) == REGNO (operands[0]) + 1
&& (TARGET_H8300SX || (REGNO (operands[0]) & 1) == 0))"
[(parallel [(set (mem:SI (plus:HI (reg:HI SP_REG) (const_int -4)))
(match_dup 0))
(set (mem:SI (plus:HI (reg:HI SP_REG) (const_int -8)))
(match_dup 1))
(set (reg:HI SP_REG)
(plus:HI (reg:HI SP_REG)
(const_int -8)))])]
"")
 
;; Turn
;;
;; mov.w #2,r0
;; add.w r7,r0 (6 bytes)
;;
;; into
;;
;; mov.w r7,r0
;; adds #2,r0 (4 bytes)
 
(define_peephole2
[(set (match_operand:HI 0 "register_operand" "")
(match_operand:HI 1 "const_int_operand" ""))
(set (match_dup 0)
(plus:HI (match_dup 0)
(match_operand:HI 2 "register_operand" "")))]
"REG_P (operands[0]) && REG_P (operands[2])
&& REGNO (operands[0]) != REGNO (operands[2])
&& (CONST_OK_FOR_J (INTVAL (operands[1]))
|| CONST_OK_FOR_L (INTVAL (operands[1]))
|| CONST_OK_FOR_N (INTVAL (operands[1])))"
[(set (match_dup 0)
(match_dup 2))
(set (match_dup 0)
(plus:HI (match_dup 0)
(match_dup 1)))]
"")
 
;; Turn
;;
;; sub.l er0,er0
;; add.b #4,r0l
;; add.l er7,er0 (6 bytes)
;;
;; into
;;
;; mov.l er7,er0
;; adds #4,er0 (4 bytes)
 
(define_peephole2
[(set (match_operand:SI 0 "register_operand" "")
(match_operand:SI 1 "const_int_operand" ""))
(set (match_dup 0)
(plus:SI (match_dup 0)
(match_operand:SI 2 "register_operand" "")))]
"(TARGET_H8300H || TARGET_H8300S)
&& REG_P (operands[0]) && REG_P (operands[2])
&& REGNO (operands[0]) != REGNO (operands[2])
&& (CONST_OK_FOR_L (INTVAL (operands[1]))
|| CONST_OK_FOR_N (INTVAL (operands[1])))"
[(set (match_dup 0)
(match_dup 2))
(set (match_dup 0)
(plus:SI (match_dup 0)
(match_dup 1)))]
"")
 
;; Turn
;;
;; mov.l er7,er0
;; add.l #10,er0 (takes 8 bytes)
;;
;; into
;;
;; sub.l er0,er0
;; add.b #10,r0l
;; add.l er7,er0 (takes 6 bytes)
 
(define_peephole2
[(set (match_operand:SI 0 "register_operand" "")
(match_operand:SI 1 "register_operand" ""))
(set (match_dup 0)
(plus:SI (match_dup 0)
(match_operand:SI 2 "const_int_operand" "")))]
"(TARGET_H8300H || TARGET_H8300S)
&& REG_P (operands[0]) && REG_P (operands[1])
&& REGNO (operands[0]) != REGNO (operands[1])
&& !CONST_OK_FOR_L (INTVAL (operands[2]))
&& !CONST_OK_FOR_N (INTVAL (operands[2]))
&& ((INTVAL (operands[2]) & 0xff) == INTVAL (operands[2])
|| (INTVAL (operands[2]) & 0xff00) == INTVAL (operands[2])
|| INTVAL (operands[2]) == 0xffff
|| INTVAL (operands[2]) == 0xfffe)"
[(set (match_dup 0)
(match_dup 2))
(set (match_dup 0)
(plus:SI (match_dup 0)
(match_dup 1)))]
"")
 
;; Turn
;;
;; subs #1,er4
;; mov.w r4,r4
;; bne .L2028
;;
;; into
;;
;; dec.w #1,r4
;; bne .L2028
 
(define_peephole2
[(set (match_operand:HI 0 "register_operand" "")
(plus:HI (match_dup 0)
(match_operand 1 "incdec_operand" "")))
(set (cc0)
(match_dup 0))
(set (pc)
(if_then_else (match_operator 3 "eqne_operator"
[(cc0) (const_int 0)])
(label_ref (match_operand 2 "" ""))
(pc)))]
"TARGET_H8300H || TARGET_H8300S"
[(set (match_operand:HI 0 "register_operand" "")
(unspec:HI [(match_dup 0)
(match_dup 1)]
UNSPEC_INCDEC))
(set (cc0)
(match_dup 0))
(set (pc)
(if_then_else (match_op_dup 3 [(cc0) (const_int 0)])
(label_ref (match_dup 2))
(pc)))]
"")
 
;; The SImode version of the previous pattern.
 
(define_peephole2
[(set (match_operand:SI 0 "register_operand" "")
(plus:SI (match_dup 0)
(match_operand 1 "incdec_operand" "")))
(set (cc0)
(match_dup 0))
(set (pc)
(if_then_else (match_operator 3 "eqne_operator"
[(cc0) (const_int 0)])
(label_ref (match_operand 2 "" ""))
(pc)))]
"TARGET_H8300H || TARGET_H8300S"
[(set (match_operand:SI 0 "register_operand" "")
(unspec:SI [(match_dup 0)
(match_dup 1)]
UNSPEC_INCDEC))
(set (cc0)
(match_dup 0))
(set (pc)
(if_then_else (match_op_dup 3 [(cc0) (const_int 0)])
(label_ref (match_dup 2))
(pc)))]
"")
 
(define_peephole2
[(parallel [(set (cc0)
(zero_extract:SI (match_operand:QI 0 "register_operand" "")
(const_int 1)
(const_int 7)))
(clobber (scratch:QI))])
(set (pc)
(if_then_else (match_operator 1 "eqne_operator"
[(cc0) (const_int 0)])
(label_ref (match_operand 2 "" ""))
(pc)))]
"(TARGET_H8300H || TARGET_H8300S)"
[(set (cc0)
(match_dup 0))
(set (pc)
(if_then_else (match_op_dup 3 [(cc0) (const_int 0)])
(label_ref (match_dup 2))
(pc)))]
"operands[3] = ((GET_CODE (operands[1]) == EQ)
? gen_rtx_GE (VOIDmode, cc0_rtx, const0_rtx)
: gen_rtx_LT (VOIDmode, cc0_rtx, const0_rtx));")
 
;; The next three peephole2's will try to transform
;;
;; mov.b A,r0l (or mov.l A,er0)
;; and.l #CST,er0
;;
;; into
;;
;; sub.l er0
;; mov.b A,r0l
;; and.b #CST,r0l (if CST is not 255)
 
(define_peephole2
[(set (match_operand:QI 0 "register_operand" "")
(match_operand:QI 1 "general_operand" ""))
(set (match_operand:SI 2 "register_operand" "")
(and:SI (match_dup 2)
(const_int 255)))]
"(TARGET_H8300H || TARGET_H8300S)
&& !reg_overlap_mentioned_p (operands[2], operands[1])
&& REGNO (operands[0]) == REGNO (operands[2])"
[(set (match_dup 2)
(const_int 0))
(set (strict_low_part (match_dup 0))
(match_dup 1))]
"")
 
(define_peephole2
[(set (match_operand:SI 0 "register_operand" "")
(match_operand:SI 1 "general_operand" ""))
(set (match_dup 0)
(and:SI (match_dup 0)
(const_int 255)))]
"(TARGET_H8300H || TARGET_H8300S)
&& !reg_overlap_mentioned_p (operands[0], operands[1])
&& !(GET_CODE (operands[1]) == MEM && !offsettable_memref_p (operands[1]))
&& !(GET_CODE (operands[1]) == MEM && MEM_VOLATILE_P (operands[1]))"
[(set (match_dup 0)
(const_int 0))
(set (strict_low_part (match_dup 2))
(match_dup 3))]
"operands[2] = gen_lowpart (QImode, operands[0]);
operands[3] = gen_lowpart (QImode, operands[1]);")
 
(define_peephole2
[(set (match_operand 0 "register_operand" "")
(match_operand 1 "general_operand" ""))
(set (match_operand:SI 2 "register_operand" "")
(and:SI (match_dup 2)
(match_operand:SI 3 "const_int_qi_operand" "")))]
"(TARGET_H8300H || TARGET_H8300S)
&& (GET_MODE (operands[0]) == QImode
|| GET_MODE (operands[0]) == HImode
|| GET_MODE (operands[0]) == SImode)
&& GET_MODE (operands[0]) == GET_MODE (operands[1])
&& REGNO (operands[0]) == REGNO (operands[2])
&& !reg_overlap_mentioned_p (operands[2], operands[1])
&& !(GET_MODE (operands[1]) != QImode
&& GET_CODE (operands[1]) == MEM
&& !offsettable_memref_p (operands[1]))
&& !(GET_MODE (operands[1]) != QImode
&& GET_CODE (operands[1]) == MEM
&& MEM_VOLATILE_P (operands[1]))"
[(set (match_dup 2)
(const_int 0))
(set (strict_low_part (match_dup 4))
(match_dup 5))
(set (match_dup 2)
(and:SI (match_dup 2)
(match_dup 6)))]
"operands[4] = gen_lowpart (QImode, operands[0]);
operands[5] = gen_lowpart (QImode, operands[1]);
operands[6] = GEN_INT (~0xff | INTVAL (operands[3]));")
 
(define_peephole2
[(set (match_operand:SI 0 "register_operand" "")
(match_operand:SI 1 "register_operand" ""))
(set (match_dup 0)
(and:SI (match_dup 0)
(const_int 65280)))]
"(TARGET_H8300H || TARGET_H8300S)
&& !reg_overlap_mentioned_p (operands[0], operands[1])"
[(set (match_dup 0)
(const_int 0))
(set (zero_extract:SI (match_dup 0)
(const_int 8)
(const_int 8))
(lshiftrt:SI (match_dup 1)
(const_int 8)))]
"")
 
;; If a load of mem:SI is followed by an AND that turns off the upper
;; half, then we can load mem:HI instead.
 
(define_peephole2
[(set (match_operand:SI 0 "register_operand" "")
(match_operand:SI 1 "memory_operand" ""))
(set (match_dup 0)
(and:SI (match_dup 0)
(match_operand:SI 2 "const_int_operand" "")))]
"(TARGET_H8300H || TARGET_H8300S)
&& !MEM_VOLATILE_P (operands[1])
&& offsettable_memref_p (operands[1])
&& (INTVAL (operands[2]) & ~0xffff) == 0
&& INTVAL (operands[2]) != 255"
[(set (match_dup 3)
(match_dup 4))
(set (match_dup 0)
(and:SI (match_dup 0)
(match_dup 2)))]
"operands[3] = gen_lowpart (HImode, operands[0]);
operands[4] = gen_lowpart (HImode, operands[1]);")
 
;; (compare (reg:HI) (const_int)) takes 4 bytes, so we try to achieve
;; the equivalent with shorter sequences. Here is the summary. Cases
;; are grouped for each define_peephole2.
;;
;; reg const_int use insn
;; --------------------------------------------------------
;; dead -2 eq/ne inc.l
;; dead -1 eq/ne inc.l
;; dead 1 eq/ne dec.l
;; dead 2 eq/ne dec.l
;;
;; dead 1 ge/lt shar.l
;; dead 3 (H8S) ge/lt shar.l
;;
;; dead 1 geu/ltu shar.l
;; dead 3 (H8S) geu/ltu shar.l
;;
;; ---- 255 ge/lt mov.b
;;
;; ---- 255 geu/ltu mov.b
 
;; Transform
;;
;; cmp.w #1,r0
;; bne .L1
;;
;; into
;;
;; dec.w #1,r0
;; bne .L1
 
(define_peephole2
[(set (cc0)
(compare (match_operand:HI 0 "register_operand" "")
(match_operand:HI 1 "incdec_operand" "")))
(set (pc)
(if_then_else (match_operator 3 "eqne_operator"
[(cc0) (const_int 0)])
(label_ref (match_operand 2 "" ""))
(pc)))]
"(TARGET_H8300H || TARGET_H8300S)
&& peep2_reg_dead_p (1, operands[0])"
[(set (match_dup 0)
(unspec:HI [(match_dup 0)
(match_dup 4)]
UNSPEC_INCDEC))
(set (cc0)
(match_dup 0))
(set (pc)
(if_then_else (match_op_dup 3 [(cc0) (const_int 0)])
(label_ref (match_dup 2))
(pc)))]
"operands[4] = GEN_INT (- INTVAL (operands[1]));")
 
;; Transform
;;
;; cmp.w #1,r0
;; bgt .L1
;;
;; into
;;
;; shar.w r0
;; bgt .L1
 
(define_peephole2
[(set (cc0)
(compare (match_operand:HI 0 "register_operand" "")
(match_operand:HI 1 "const_int_operand" "")))
(set (pc)
(if_then_else (match_operator 2 "gtle_operator"
[(cc0) (const_int 0)])
(label_ref (match_operand 3 "" ""))
(pc)))]
"(TARGET_H8300H || TARGET_H8300S)
&& peep2_reg_dead_p (1, operands[0])
&& (INTVAL (operands[1]) == 1
|| (TARGET_H8300S && INTVAL (operands[1]) == 3))"
[(parallel [(set (match_dup 0)
(ashiftrt:HI (match_dup 0)
(match_dup 4)))
(clobber (scratch:QI))])
(set (cc0)
(match_dup 0))
(set (pc)
(if_then_else (match_dup 2)
(label_ref (match_dup 3))
(pc)))]
"operands[4] = GEN_INT (exact_log2 (INTVAL (operands[1]) + 1));")
 
;; Transform
;;
;; cmp.w #1,r0
;; bhi .L1
;;
;; into
;;
;; shar.w r0
;; bne .L1
 
(define_peephole2
[(set (cc0)
(compare (match_operand:HI 0 "register_operand" "")
(match_operand:HI 1 "const_int_operand" "")))
(set (pc)
(if_then_else (match_operator 2 "gtuleu_operator"
[(cc0) (const_int 0)])
(label_ref (match_operand 3 "" ""))
(pc)))]
"(TARGET_H8300H || TARGET_H8300S)
&& peep2_reg_dead_p (1, operands[0])
&& (INTVAL (operands[1]) == 1
|| (TARGET_H8300S && INTVAL (operands[1]) == 3))"
[(parallel [(set (match_dup 0)
(ashiftrt:HI (match_dup 0)
(match_dup 4)))
(clobber (scratch:QI))])
(set (cc0)
(match_dup 0))
(set (pc)
(if_then_else (match_dup 5)
(label_ref (match_dup 3))
(pc)))]
{
operands[4] = GEN_INT (exact_log2 (INTVAL (operands[1]) + 1));
operands[5] = gen_rtx_fmt_ee (GET_CODE (operands[2]) == GTU ? NE : EQ,
VOIDmode,
cc0_rtx,
const0_rtx);
})
 
;; Transform
;;
;; cmp.w #255,r0
;; bgt .L1
;;
;; into
;;
;; mov.b r0h,r0h
;; bgt .L1
 
(define_peephole2
[(set (cc0)
(compare (match_operand:HI 0 "register_operand" "")
(const_int 255)))
(set (pc)
(if_then_else (match_operator 1 "gtle_operator"
[(cc0) (const_int 0)])
(label_ref (match_operand 2 "" ""))
(pc)))]
"TARGET_H8300H || TARGET_H8300S"
[(set (cc0)
(and:HI (match_dup 0)
(const_int -256)))
(set (pc)
(if_then_else (match_dup 1)
(label_ref (match_dup 2))
(pc)))]
"")
 
;; Transform
;;
;; cmp.w #255,r0
;; bhi .L1
;;
;; into
;;
;; mov.b r0h,r0h
;; bne .L1
 
(define_peephole2
[(set (cc0)
(compare (match_operand:HI 0 "register_operand" "")
(const_int 255)))
(set (pc)
(if_then_else (match_operator 1 "gtuleu_operator"
[(cc0) (const_int 0)])
(label_ref (match_operand 2 "" ""))
(pc)))]
"TARGET_H8300H || TARGET_H8300S"
[(set (cc0)
(and:HI (match_dup 0)
(const_int -256)))
(set (pc)
(if_then_else (match_dup 3)
(label_ref (match_dup 2))
(pc)))]
{
operands[3] = gen_rtx_fmt_ee (GET_CODE (operands[1]) == GTU ? NE : EQ,
VOIDmode,
cc0_rtx,
const0_rtx);
})
 
;; (compare (reg:SI) (const_int)) takes 6 bytes, so we try to achieve
;; the equivalent with shorter sequences. Here is the summary. Cases
;; are grouped for each define_peephole2.
;;
;; reg const_int use insn
;; --------------------------------------------------------
;; live -2 eq/ne copy and inc.l
;; live -1 eq/ne copy and inc.l
;; live 1 eq/ne copy and dec.l
;; live 2 eq/ne copy and dec.l
;;
;; dead -2 eq/ne inc.l
;; dead -1 eq/ne inc.l
;; dead 1 eq/ne dec.l
;; dead 2 eq/ne dec.l
;;
;; dead -131072 eq/ne inc.w and test
;; dead -65536 eq/ne inc.w and test
;; dead 65536 eq/ne dec.w and test
;; dead 131072 eq/ne dec.w and test
;;
;; dead 0x000000?? except 1 and 2 eq/ne xor.b and test
;; dead 0x0000??00 eq/ne xor.b and test
;; dead 0x0000ffff eq/ne not.w and test
;;
;; dead 0xffffff?? except -1 and -2 eq/ne xor.b and not.l
;; dead 0xffff??ff eq/ne xor.b and not.l
;; dead 0x40000000 (H8S) eq/ne rotl.l and dec.l
;; dead 0x80000000 eq/ne rotl.l and dec.l
;;
;; live 1 ge/lt copy and shar.l
;; live 3 (H8S) ge/lt copy and shar.l
;;
;; live 1 geu/ltu copy and shar.l
;; live 3 (H8S) geu/ltu copy and shar.l
;;
;; dead 1 ge/lt shar.l
;; dead 3 (H8S) ge/lt shar.l
;;
;; dead 1 geu/ltu shar.l
;; dead 3 (H8S) geu/ltu shar.l
;;
;; dead 3 (H8/300H) ge/lt and.b and test
;; dead 7 ge/lt and.b and test
;; dead 15 ge/lt and.b and test
;; dead 31 ge/lt and.b and test
;; dead 63 ge/lt and.b and test
;; dead 127 ge/lt and.b and test
;; dead 255 ge/lt and.b and test
;;
;; dead 3 (H8/300H) geu/ltu and.b and test
;; dead 7 geu/ltu and.b and test
;; dead 15 geu/ltu and.b and test
;; dead 31 geu/ltu and.b and test
;; dead 63 geu/ltu and.b and test
;; dead 127 geu/ltu and.b and test
;; dead 255 geu/ltu and.b and test
;;
;; ---- 65535 ge/lt mov.w
;;
;; ---- 65535 geu/ltu mov.w
 
;; Transform
;;
;; cmp.l #1,er0
;; beq .L1
;;
;; into
;;
;; dec.l #1,er0
;; beq .L1
 
(define_peephole2
[(set (cc0)
(compare (match_operand:SI 0 "register_operand" "")
(match_operand:SI 1 "incdec_operand" "")))
(set (pc)
(if_then_else (match_operator 3 "eqne_operator"
[(cc0) (const_int 0)])
(label_ref (match_operand 2 "" ""))
(pc)))]
"(TARGET_H8300H || TARGET_H8300S)
&& peep2_reg_dead_p (1, operands[0])"
[(set (match_dup 0)
(unspec:SI [(match_dup 0)
(match_dup 4)]
UNSPEC_INCDEC))
(set (cc0)
(match_dup 0))
(set (pc)
(if_then_else (match_op_dup 3 [(cc0) (const_int 0)])
(label_ref (match_dup 2))
(pc)))]
"operands[4] = GEN_INT (- INTVAL (operands[1]));")
 
;; Transform
;;
;; cmp.l #65536,er0
;; beq .L1
;;
;; into
;;
;; dec.l #1,e0
;; beq .L1
 
(define_peephole2
[(set (cc0)
(compare (match_operand:SI 0 "register_operand" "")
(match_operand:SI 1 "const_int_operand" "")))
(set (pc)
(if_then_else (match_operator 3 "eqne_operator"
[(cc0) (const_int 0)])
(label_ref (match_operand 2 "" ""))
(pc)))]
"(TARGET_H8300H || TARGET_H8300S)
&& peep2_reg_dead_p (1, operands[0])
&& (INTVAL (operands[1]) == -131072
|| INTVAL (operands[1]) == -65536
|| INTVAL (operands[1]) == 65536
|| INTVAL (operands[1]) == 131072)"
[(set (match_dup 0)
(plus:SI (match_dup 0)
(match_dup 4)))
(set (cc0)
(match_dup 0))
(set (pc)
(if_then_else (match_op_dup 3 [(cc0) (const_int 0)])
(label_ref (match_dup 2))
(pc)))]
"operands[4] = GEN_INT (- INTVAL (operands[1]));")
 
;; Transform
;;
;; cmp.l #100,er0
;; beq .L1
;;
;; into
;;
;; xor.b #100,er0
;; mov.l er0,er0
;; beq .L1
 
(define_peephole2
[(set (cc0)
(compare (match_operand:SI 0 "register_operand" "")
(match_operand:SI 1 "const_int_operand" "")))
(set (pc)
(if_then_else (match_operator 3 "eqne_operator"
[(cc0) (const_int 0)])
(label_ref (match_operand 2 "" ""))
(pc)))]
"(TARGET_H8300H || TARGET_H8300S)
&& peep2_reg_dead_p (1, operands[0])
&& ((INTVAL (operands[1]) & 0x00ff) == INTVAL (operands[1])
|| (INTVAL (operands[1]) & 0xff00) == INTVAL (operands[1])
|| INTVAL (operands[1]) == 0x0000ffff)
&& INTVAL (operands[1]) != 1
&& INTVAL (operands[1]) != 2"
[(set (match_dup 0)
(xor:SI (match_dup 0)
(match_dup 1)))
(set (cc0)
(match_dup 0))
(set (pc)
(if_then_else (match_op_dup 3 [(cc0) (const_int 0)])
(label_ref (match_dup 2))
(pc)))]
"")
 
;; Transform
;;
;; cmp.l #-100,er0
;; beq .L1
;;
;; into
;;
;; xor.b #99,er0
;; not.l er0
;; beq .L1
 
(define_peephole2
[(set (cc0)
(compare (match_operand:SI 0 "register_operand" "")
(match_operand:SI 1 "const_int_operand" "")))
(set (pc)
(if_then_else (match_operator 3 "eqne_operator"
[(cc0) (const_int 0)])
(label_ref (match_operand 2 "" ""))
(pc)))]
"(TARGET_H8300H || TARGET_H8300S)
&& peep2_reg_dead_p (1, operands[0])
&& ((INTVAL (operands[1]) | 0x00ff) == -1
|| (INTVAL (operands[1]) | 0xff00) == -1)
&& INTVAL (operands[1]) != -1
&& INTVAL (operands[1]) != -2"
[(set (match_dup 0)
(xor:SI (match_dup 0)
(match_dup 4)))
(set (match_dup 0)
(not:SI (match_dup 0)))
(set (cc0)
(match_dup 0))
(set (pc)
(if_then_else (match_op_dup 3 [(cc0) (const_int 0)])
(label_ref (match_dup 2))
(pc)))]
"operands[4] = GEN_INT (INTVAL (operands[1]) ^ -1);")
 
;; Transform
;;
;; cmp.l #-2147483648,er0
;; beq .L1
;;
;; into
;;
;; rotl.l er0
;; dec.l #1,er0
;; beq .L1
 
(define_peephole2
[(set (cc0)
(compare (match_operand:SI 0 "register_operand" "")
(match_operand:SI 1 "const_int_operand" "")))
(set (pc)
(if_then_else (match_operator 3 "eqne_operator"
[(cc0) (const_int 0)])
(label_ref (match_operand 2 "" ""))
(pc)))]
"(TARGET_H8300H || TARGET_H8300S)
&& peep2_reg_dead_p (1, operands[0])
&& (INTVAL (operands[1]) == -2147483647 - 1
|| (TARGET_H8300S && INTVAL (operands[1]) == 1073741824))"
[(set (match_dup 0)
(rotate:SI (match_dup 0)
(match_dup 4)))
(set (match_dup 0)
(unspec:SI [(match_dup 0)
(const_int -1)]
UNSPEC_INCDEC))
(set (cc0)
(match_dup 0))
(set (pc)
(if_then_else (match_op_dup 3 [(cc0) (const_int 0)])
(label_ref (match_dup 2))
(pc)))]
"operands[4] = GEN_INT (INTVAL (operands[1]) == -2147483647 - 1 ? 1 : 2);")
 
;; Transform
;;
;; cmp.l #1,er0
;; bgt .L1
;;
;; into
;;
;; mov.l er0,er1
;; shar.l er1
;; bgt .L1
 
;; We avoid this transformation if we see more than one copy of the
;; same compare insn immediately before this one.
 
(define_peephole2
[(match_scratch:SI 4 "r")
(set (cc0)
(compare (match_operand:SI 0 "register_operand" "")
(match_operand:SI 1 "const_int_operand" "")))
(set (pc)
(if_then_else (match_operator 2 "gtle_operator"
[(cc0) (const_int 0)])
(label_ref (match_operand 3 "" ""))
(pc)))]
"(TARGET_H8300H || TARGET_H8300S)
&& !peep2_reg_dead_p (1, operands[0])
&& (INTVAL (operands[1]) == 1
|| (TARGET_H8300S && INTVAL (operands[1]) == 3))
&& !same_cmp_preceding_p (insn)"
[(set (match_dup 4)
(match_dup 0))
(parallel [(set (match_dup 4)
(ashiftrt:SI (match_dup 4)
(match_dup 5)))
(clobber (scratch:QI))])
(set (cc0)
(match_dup 4))
(set (pc)
(if_then_else (match_dup 2)
(label_ref (match_dup 3))
(pc)))]
"operands[5] = GEN_INT (exact_log2 (INTVAL (operands[1]) + 1));")
 
;; Transform
;;
;; cmp.l #1,er0
;; bhi .L1
;;
;; into
;;
;; mov.l er0,er1
;; shar.l er1
;; bne .L1
 
;; We avoid this transformation if we see more than one copy of the
;; same compare insn immediately before this one.
 
(define_peephole2
[(match_scratch:SI 4 "r")
(set (cc0)
(compare (match_operand:SI 0 "register_operand" "")
(match_operand:SI 1 "const_int_operand" "")))
(set (pc)
(if_then_else (match_operator 2 "gtuleu_operator"
[(cc0) (const_int 0)])
(label_ref (match_operand 3 "" ""))
(pc)))]
"(TARGET_H8300H || TARGET_H8300S)
&& !peep2_reg_dead_p (1, operands[0])
&& (INTVAL (operands[1]) == 1
|| (TARGET_H8300S && INTVAL (operands[1]) == 3))
&& !same_cmp_preceding_p (insn)"
[(set (match_dup 4)
(match_dup 0))
(parallel [(set (match_dup 4)
(ashiftrt:SI (match_dup 4)
(match_dup 5)))
(clobber (scratch:QI))])
(set (cc0)
(match_dup 4))
(set (pc)
(if_then_else (match_dup 6)
(label_ref (match_dup 3))
(pc)))]
{
operands[5] = GEN_INT (exact_log2 (INTVAL (operands[1]) + 1));
operands[6] = gen_rtx_fmt_ee (GET_CODE (operands[2]) == GTU ? NE : EQ,
VOIDmode,
cc0_rtx,
const0_rtx);
})
 
;; Transform
;;
;; cmp.l #1,er0
;; bgt .L1
;;
;; into
;;
;; shar.l er0
;; bgt .L1
 
(define_peephole2
[(set (cc0)
(compare (match_operand:SI 0 "register_operand" "")
(match_operand:SI 1 "const_int_operand" "")))
(set (pc)
(if_then_else (match_operator 2 "gtle_operator"
[(cc0) (const_int 0)])
(label_ref (match_operand 3 "" ""))
(pc)))]
"(TARGET_H8300H || TARGET_H8300S)
&& peep2_reg_dead_p (1, operands[0])
&& (INTVAL (operands[1]) == 1
|| (TARGET_H8300S && INTVAL (operands[1]) == 3))"
[(parallel [(set (match_dup 0)
(ashiftrt:SI (match_dup 0)
(match_dup 4)))
(clobber (scratch:QI))])
(set (cc0)
(match_dup 0))
(set (pc)
(if_then_else (match_dup 2)
(label_ref (match_dup 3))
(pc)))]
"operands[4] = GEN_INT (exact_log2 (INTVAL (operands[1]) + 1));")
 
;; Transform
;;
;; cmp.l #1,er0
;; bhi .L1
;;
;; into
;;
;; shar.l er0
;; bne .L1
 
(define_peephole2
[(set (cc0)
(compare (match_operand:SI 0 "register_operand" "")
(match_operand:SI 1 "const_int_operand" "")))
(set (pc)
(if_then_else (match_operator 2 "gtuleu_operator"
[(cc0) (const_int 0)])
(label_ref (match_operand 3 "" ""))
(pc)))]
"(TARGET_H8300H || TARGET_H8300S)
&& peep2_reg_dead_p (1, operands[0])
&& (INTVAL (operands[1]) == 1
|| (TARGET_H8300S && INTVAL (operands[1]) == 3))"
[(parallel [(set (match_dup 0)
(ashiftrt:SI (match_dup 0)
(match_dup 4)))
(clobber (scratch:QI))])
(set (cc0)
(match_dup 0))
(set (pc)
(if_then_else (match_dup 5)
(label_ref (match_dup 3))
(pc)))]
{
operands[4] = GEN_INT (exact_log2 (INTVAL (operands[1]) + 1));
operands[5] = gen_rtx_fmt_ee (GET_CODE (operands[2]) == GTU ? NE : EQ,
VOIDmode,
cc0_rtx,
const0_rtx);
})
 
;; Transform
;;
;; cmp.l #15,er0
;; bgt .L1
;;
;; into
;;
;; and #240,r0l
;; mov.l er0,er0
;; bgt .L1
 
(define_peephole2
[(set (cc0)
(compare (match_operand:SI 0 "register_operand" "")
(match_operand:SI 1 "const_int_operand" "")))
(set (pc)
(if_then_else (match_operator 2 "gtle_operator"
[(cc0) (const_int 0)])
(label_ref (match_operand 3 "" ""))
(pc)))]
"(TARGET_H8300H || TARGET_H8300S)
&& peep2_reg_dead_p (1, operands[0])
&& ((TARGET_H8300H && INTVAL (operands[1]) == 3)
|| INTVAL (operands[1]) == 7
|| INTVAL (operands[1]) == 15
|| INTVAL (operands[1]) == 31
|| INTVAL (operands[1]) == 63
|| INTVAL (operands[1]) == 127
|| INTVAL (operands[1]) == 255)"
[(set (match_dup 0)
(and:SI (match_dup 0)
(match_dup 4)))
(set (cc0)
(match_dup 0))
(set (pc)
(if_then_else (match_dup 2)
(label_ref (match_dup 3))
(pc)))]
"operands[4] = GEN_INT (~INTVAL (operands[1]));")
 
;; Transform
;;
;; cmp.l #15,er0
;; bhi .L1
;;
;; into
;;
;; and #240,r0l
;; mov.l er0,er0
;; bne .L1
 
(define_peephole2
[(set (cc0)
(compare (match_operand:SI 0 "register_operand" "")
(match_operand:SI 1 "const_int_operand" "")))
(set (pc)
(if_then_else (match_operator 2 "gtuleu_operator"
[(cc0) (const_int 0)])
(label_ref (match_operand 3 "" ""))
(pc)))]
"(TARGET_H8300H || TARGET_H8300S)
&& peep2_reg_dead_p (1, operands[0])
&& ((TARGET_H8300H && INTVAL (operands[1]) == 3)
|| INTVAL (operands[1]) == 7
|| INTVAL (operands[1]) == 15
|| INTVAL (operands[1]) == 31
|| INTVAL (operands[1]) == 63
|| INTVAL (operands[1]) == 127
|| INTVAL (operands[1]) == 255)"
[(set (match_dup 0)
(and:SI (match_dup 0)
(match_dup 4)))
(set (cc0)
(match_dup 0))
(set (pc)
(if_then_else (match_dup 5)
(label_ref (match_dup 3))
(pc)))]
{
operands[4] = GEN_INT (~INTVAL (operands[1]));
operands[5] = gen_rtx_fmt_ee (GET_CODE (operands[2]) == GTU ? NE : EQ,
VOIDmode,
cc0_rtx,
const0_rtx);
})
 
;; Transform
;;
;; cmp.l #65535,er0
;; bgt .L1
;;
;; into
;;
;; mov.l e0,e0
;; bgt .L1
 
(define_peephole2
[(set (cc0)
(compare (match_operand:SI 0 "register_operand" "")
(const_int 65535)))
(set (pc)
(if_then_else (match_operator 1 "gtle_operator"
[(cc0) (const_int 0)])
(label_ref (match_operand 2 "" ""))
(pc)))]
"TARGET_H8300H || TARGET_H8300S"
[(set (cc0)
(and:SI (match_dup 0)
(const_int -65536)))
(set (pc)
(if_then_else (match_dup 1)
(label_ref (match_dup 2))
(pc)))]
"")
 
;; Transform
;;
;; cmp.l #65535,er0
;; bhi .L1
;;
;; into
;;
;; mov.l e0,e0
;; bne .L1
 
(define_peephole2
[(set (cc0)
(compare (match_operand:SI 0 "register_operand" "")
(const_int 65535)))
(set (pc)
(if_then_else (match_operator 1 "gtuleu_operator"
[(cc0) (const_int 0)])
(label_ref (match_operand 2 "" ""))
(pc)))]
"TARGET_H8300H || TARGET_H8300S"
[(set (cc0)
(and:SI (match_dup 0)
(const_int -65536)))
(set (pc)
(if_then_else (match_dup 3)
(label_ref (match_dup 2))
(pc)))]
{
operands[3] = gen_rtx_fmt_ee (GET_CODE (operands[1]) == GTU ? NE : EQ,
VOIDmode,
cc0_rtx,
const0_rtx);
})
 
;; Transform
;;
;; cmp.l #1,er0
;; beq .L1
;;
;; into
;;
;; mov.l er0,er1
;; dec.l #1,er1
;; beq .L1
 
;; We avoid this transformation if we see more than one copy of the
;; same compare insn.
 
(define_peephole2
[(match_scratch:SI 4 "r")
(set (cc0)
(compare (match_operand:SI 0 "register_operand" "")
(match_operand:SI 1 "incdec_operand" "")))
(set (pc)
(if_then_else (match_operator 3 "eqne_operator"
[(cc0) (const_int 0)])
(label_ref (match_operand 2 "" ""))
(pc)))]
"(TARGET_H8300H || TARGET_H8300S)
&& !peep2_reg_dead_p (1, operands[0])
&& !same_cmp_following_p (insn)"
[(set (match_dup 4)
(match_dup 0))
(set (match_dup 4)
(unspec:SI [(match_dup 4)
(match_dup 5)]
UNSPEC_INCDEC))
(set (cc0)
(match_dup 4))
(set (pc)
(if_then_else (match_op_dup 3 [(cc0) (const_int 0)])
(label_ref (match_dup 2))
(pc)))]
"operands[5] = GEN_INT (- INTVAL (operands[1]));")
 
;; Narrow the mode of testing if possible.
 
(define_peephole2
[(set (match_operand:HI 0 "register_operand" "")
(and:HI (match_dup 0)
(match_operand:HI 1 "const_int_qi_operand" "")))
(set (cc0)
(match_dup 0))
(set (pc)
(if_then_else (match_operator 3 "eqne_operator"
[(cc0) (const_int 0)])
(label_ref (match_operand 2 "" ""))
(pc)))]
"peep2_reg_dead_p (2, operands[0])"
[(set (match_dup 4)
(and:QI (match_dup 4)
(match_dup 5)))
(set (cc0)
(match_dup 4))
(set (pc)
(if_then_else (match_op_dup 3 [(cc0) (const_int 0)])
(label_ref (match_dup 2))
(pc)))]
"operands[4] = gen_rtx_REG (QImode, REGNO (operands[0]));
operands[5] = gen_int_mode (INTVAL (operands[1]), QImode);")
 
(define_peephole2
[(set (match_operand:SI 0 "register_operand" "")
(and:SI (match_dup 0)
(match_operand:SI 1 "const_int_qi_operand" "")))
(set (cc0)
(match_dup 0))
(set (pc)
(if_then_else (match_operator 3 "eqne_operator"
[(cc0) (const_int 0)])
(label_ref (match_operand 2 "" ""))
(pc)))]
"peep2_reg_dead_p (2, operands[0])"
[(set (match_dup 4)
(and:QI (match_dup 4)
(match_dup 5)))
(set (cc0)
(match_dup 4))
(set (pc)
(if_then_else (match_op_dup 3 [(cc0) (const_int 0)])
(label_ref (match_dup 2))
(pc)))]
"operands[4] = gen_rtx_REG (QImode, REGNO (operands[0]));
operands[5] = gen_int_mode (INTVAL (operands[1]), QImode);")
 
(define_peephole2
[(set (match_operand:SI 0 "register_operand" "")
(and:SI (match_dup 0)
(match_operand:SI 1 "const_int_hi_operand" "")))
(set (cc0)
(match_dup 0))
(set (pc)
(if_then_else (match_operator 3 "eqne_operator"
[(cc0) (const_int 0)])
(label_ref (match_operand 2 "" ""))
(pc)))]
"peep2_reg_dead_p (2, operands[0])"
[(set (match_dup 4)
(and:HI (match_dup 4)
(match_dup 5)))
(set (cc0)
(match_dup 4))
(set (pc)
(if_then_else (match_op_dup 3 [(cc0) (const_int 0)])
(label_ref (match_dup 2))
(pc)))]
"operands[4] = gen_rtx_REG (HImode, REGNO (operands[0]));
operands[5] = gen_int_mode (INTVAL (operands[1]), HImode);")
 
(define_peephole2
[(set (match_operand:SI 0 "register_operand" "")
(and:SI (match_dup 0)
(match_operand:SI 1 "const_int_qi_operand" "")))
(set (match_dup 0)
(xor:SI (match_dup 0)
(match_operand:SI 2 "const_int_qi_operand" "")))
(set (cc0)
(match_dup 0))
(set (pc)
(if_then_else (match_operator 4 "eqne_operator"
[(cc0) (const_int 0)])
(label_ref (match_operand 3 "" ""))
(pc)))]
"peep2_reg_dead_p (3, operands[0])
&& (~INTVAL (operands[1]) & INTVAL (operands[2])) == 0"
[(set (match_dup 5)
(and:QI (match_dup 5)
(match_dup 6)))
(set (match_dup 5)
(xor:QI (match_dup 5)
(match_dup 7)))
(set (cc0)
(match_dup 5))
(set (pc)
(if_then_else (match_op_dup 4 [(cc0) (const_int 0)])
(label_ref (match_dup 3))
(pc)))]
"operands[5] = gen_rtx_REG (QImode, REGNO (operands[0]));
operands[6] = gen_int_mode (INTVAL (operands[1]), QImode);
operands[7] = gen_int_mode (INTVAL (operands[2]), QImode);")
 
;; These triggers right at the end of allocation of locals in the
;; prologue (and possibly at other places).
 
;; stack adjustment of -4, generate one push
;;
;; before : 6 bytes, 10 clocks
;; after : 4 bytes, 10 clocks
 
(define_peephole2
[(set (reg:SI SP_REG)
(plus:SI (reg:SI SP_REG)
(const_int -4)))
(set (mem:SI (reg:SI SP_REG))
(match_operand:SI 0 "register_operand" ""))]
"(TARGET_H8300H || TARGET_H8300S) && !TARGET_NORMAL_MODE
&& REGNO (operands[0]) != SP_REG"
[(set (mem:SI (pre_dec:SI (reg:SI SP_REG)))
(match_dup 0))]
"")
 
;; stack adjustment of -12, generate one push
;;
;; before : 10 bytes, 14 clocks
;; after : 8 bytes, 14 clocks
 
(define_peephole2
[(set (reg:SI SP_REG)
(plus:SI (reg:SI SP_REG)
(const_int -12)))
(set (mem:SI (reg:SI SP_REG))
(match_operand:SI 0 "register_operand" ""))]
"(TARGET_H8300H || TARGET_H8300S) && !TARGET_NORMAL_MODE
&& REGNO (operands[0]) != SP_REG"
[(set (reg:SI SP_REG)
(plus:SI (reg:SI SP_REG)
(const_int -4)))
(set (reg:SI SP_REG)
(plus:SI (reg:SI SP_REG)
(const_int -4)))
(set (mem:SI (pre_dec:SI (reg:SI SP_REG)))
(match_dup 0))]
"")
 
;; Transform
;;
;; mov dst,reg
;; op src,reg
;; mov reg,dst
;;
;; into
;;
;; op src,dst
;;
;; if "reg" dies at the end of the sequence.
(define_peephole2
[(set (match_operand 0 "register_operand" "")
(match_operand 1 "memory_operand" ""))
(set (match_dup 0)
(match_operator 2 "h8sx_binary_memory_operator"
[(match_dup 0)
(match_operand 3 "h8300_src_operand" "")]))
(set (match_operand 4 "memory_operand" "")
(match_dup 0))]
"0 /* Disable because it breaks compiling fp-bit.c. */
&& TARGET_H8300SX
&& peep2_reg_dead_p (3, operands[0])
&& !reg_overlap_mentioned_p (operands[0], operands[3])
&& !reg_overlap_mentioned_p (operands[0], operands[4])
&& h8sx_mergeable_memrefs_p (operands[4], operands[1])"
[(set (match_dup 4)
(match_dup 5))]
{
operands[5] = shallow_copy_rtx (operands[2]);
XEXP (operands[5], 0) = operands[1];
})
 
;; Transform
;;
;; mov src,reg
;; op reg,dst
;;
;; into
;;
;; op src,dst
;;
;; if "reg" dies in the second insn.
(define_peephole2
[(set (match_operand 0 "register_operand" "")
(match_operand 1 "h8300_src_operand" ""))
(set (match_operand 2 "h8300_dst_operand" "")
(match_operator 3 "h8sx_binary_memory_operator"
[(match_operand 4 "h8300_dst_operand" "")
(match_dup 0)]))]
"0 /* Disable because it breaks compiling fp-bit.c. */
&& TARGET_H8300SX
&& peep2_reg_dead_p (2, operands[0])
&& !reg_overlap_mentioned_p (operands[0], operands[4])"
[(set (match_dup 2)
(match_dup 5))]
{
operands[5] = shallow_copy_rtx (operands[3]);
XEXP (operands[5], 1) = operands[1];
})
 
;; Transform
;;
;; mov dst,reg
;; op reg
;; mov reg,dst
;;
;; into
;;
;; op dst
;;
;; if "reg" dies at the end of the sequence.
(define_peephole2
[(set (match_operand 0 "register_operand" "")
(match_operand 1 "memory_operand" ""))
(set (match_dup 0)
(match_operator 2 "h8sx_unary_memory_operator"
[(match_dup 0)]))
(set (match_operand 3 "memory_operand" "")
(match_dup 0))]
"TARGET_H8300SX
&& peep2_reg_dead_p (3, operands[0])
&& !reg_overlap_mentioned_p (operands[0], operands[3])
&& h8sx_mergeable_memrefs_p (operands[3], operands[1])"
[(set (match_dup 3)
(match_dup 4))]
{
operands[4] = shallow_copy_rtx (operands[2]);
XEXP (operands[4], 0) = operands[1];
})
 
;; Transform
;;
;; mov src1,reg
;; cmp reg,src2
;;
;; into
;;
;; cmp src1,src2
;;
;; if "reg" dies in the comparison.
(define_peephole2
[(set (match_operand 0 "register_operand" "")
(match_operand 1 "h8300_dst_operand" ""))
(set (cc0)
(compare (match_dup 0)
(match_operand 2 "h8300_src_operand" "")))]
"TARGET_H8300SX
&& peep2_reg_dead_p (2, operands[0])
&& !reg_overlap_mentioned_p (operands[0], operands[2])"
[(set (cc0)
(compare (match_dup 1)
(match_dup 2)))])
 
;; Likewise for the second operand.
(define_peephole2
[(set (match_operand 0 "register_operand" "")
(match_operand 1 "h8300_src_operand" ""))
(set (cc0)
(compare (match_operand 2 "h8300_dst_operand" "")
(match_dup 0)))]
"TARGET_H8300SX
&& peep2_reg_dead_p (2, operands[0])
&& !reg_overlap_mentioned_p (operands[0], operands[2])"
[(set (cc0)
(compare (match_dup 2)
(match_dup 1)))])
 
;; Combine two moves.
(define_peephole2
[(set (match_operand 0 "register_operand" "")
(match_operand 1 "h8300_src_operand" ""))
(set (match_operand 2 "h8300_dst_operand" "")
(match_dup 0))]
"TARGET_H8300SX
&& peep2_reg_dead_p (2, operands[0])
&& !reg_overlap_mentioned_p (operands[0], operands[2])"
[(set (match_dup 2)
(match_dup 1))])
/coff.h
0,0 → 1,51
/* Definitions of target machine for GNU compiler.
Renesas H8/300 version generating coff
Copyright (C) 2003, 2004, 2007 Free Software Foundation, Inc.
Contributed by Steve Chamberlain (sac@cygnus.com),
Jim Wilson (wilson@cygnus.com), and Doug Evans (dje@cygnus.com).
 
This file is part of GCC.
 
GCC is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3, or (at your option)
any later version.
 
GCC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
 
You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING3. If not see
<http://www.gnu.org/licenses/>. */
 
#ifndef GCC_H8300_COFF_H
#define GCC_H8300_COFF_H
 
#define SDB_DEBUGGING_INFO 1
#define SDB_DELIM "\n"
 
/* Generate a blank trailing N_SO to mark the end of the .o file, since
we can't depend upon the linker to mark .o file boundaries with
embedded stabs. */
 
#define DBX_OUTPUT_NULL_N_SO_AT_MAIN_SOURCE_FILE_END
 
/* This is how to output an assembler line
that says to advance the location counter by SIZE bytes. */
 
#define ASM_OUTPUT_IDENT(FILE, NAME) \
fprintf (FILE, "%s\"%s\"\n", IDENT_ASM_OP, NAME)
 
#define IDENT_ASM_OP "\t.ident\t"
#define INIT_SECTION_ASM_OP "\t.section .init"
#define READONLY_DATA_SECTION_ASM_OP "\t.section .rodata"
 
/* Switch into a generic section. */
#define TARGET_ASM_NAMED_SECTION h8300_asm_named_section
 
/* A bit-field declared as `int' forces `int' alignment for the struct. */
#define PCC_BITFIELD_TYPE_MATTERS 0
 
#endif /* h8300/coff.h */
/h8300.opt
0,0 → 1,62
; Options for the Renesas H8/300 port of the compiler
;
; Copyright (C) 2005, 2007 Free Software Foundation, Inc.
;
; This file is part of GCC.
;
; GCC is free software; you can redistribute it and/or modify it under
; the terms of the GNU General Public License as published by the Free
; Software Foundation; either version 3, or (at your option) any later
; version.
;
; GCC is distributed in the hope that it will be useful, but WITHOUT
; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
; or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
; License for more details.
;
; You should have received a copy of the GNU General Public License
; along with GCC; see the file COPYING3. If not see
; <http://www.gnu.org/licenses/>.
 
ms
Target Mask(H8300S_1)
Generate H8S code
 
msx
Target Mask(H8300SX)
Generate H8SX code
 
ms2600
Target Mask(MAC)
Generate H8S/2600 code
 
mint32
Target RejectNegative Mask(INT32)
Make integers 32 bits wide
 
maddresses
Target Undocumented RejectNegative Mask(ADDRESSES)
 
mquickcall
Target Mask(QUICKCALL)
Use registers for argument passing
 
mslowbyte
Target RejectNegative Mask(SLOWBYTE)
Consider access to byte sized memory slow
 
mrelax
Target RejectNegative Mask(RELAX)
Enable linker relaxing
 
mh
Target Mask(H8300H)
Generate H8/300H code
 
mn
Target Mask(NORMAL_MODE)
Enable the normal mode
 
malign-300
Target RejectNegative Mask(ALIGN_300)
Use H8/300 alignment rules
/t-h8300
0,0 → 1,43
LIB1ASMSRC = h8300/lib1funcs.asm
LIB1ASMFUNCS = _cmpsi2 _ucmpsi2 _divhi3 _divsi3 _mulhi3 _mulsi3 \
_fixunssfsi_asm
 
LIB2FUNCS_EXTRA = \
$(srcdir)/config/h8300/clzhi2.c \
$(srcdir)/config/h8300/ctzhi2.c \
$(srcdir)/config/h8300/parityhi2.c \
$(srcdir)/config/h8300/popcounthi2.c \
$(srcdir)/config/h8300/fixunssfsi.c
 
# We do not have DF type, so fake out the libgcc2 compilation.
TARGET_LIBGCC2_CFLAGS = -DDF=SF
 
# We want fine grained libraries, so use the new code to build the
# floating point emulation libraries.
FPBIT = fp-bit.c
 
fp-bit.c: $(srcdir)/config/fp-bit.c
echo '#define FLOAT' > fp-bit.c
echo '#define FLOAT_ONLY' >> fp-bit.c
echo '#define SMALL_MACHINE' >> fp-bit.c
echo '#ifdef __H8300__' >> fp-bit.c
echo '#define CMPtype HItype' >> fp-bit.c
echo '#else' >> fp-bit.c
echo '#define CMPtype SItype' >> fp-bit.c
echo '#endif' >> fp-bit.c
cat $(srcdir)/config/fp-bit.c >> fp-bit.c
 
MULTILIB_OPTIONS = mh/ms/msx mn mint32
MULTILIB_DIRNAMES = h8300h h8300s h8sx normal int32
MULTILIB_EXCEPTIONS = mint32 mn mn/mint32
 
LIBGCC = stmp-multilib
INSTALL_LIBGCC = install-multilib
 
s-config s-conditions s-flags s-codes s-constants s-emit s-recog \
s-opinit s-extract s-peep s-attr s-attrtab s-output: \
$(srcdir)/config/h8300/mova.md
 
$(srcdir)/config/h8300/mova.md: $(srcdir)/config/h8300/genmova.sh
$(SHELL) $(srcdir)/config/h8300/genmova.sh \
> $(srcdir)/config/h8300/mova.md
/lib1funcs.asm
0,0 → 1,843
;; libgcc routines for the Renesas H8/300 CPU.
;; Contributed by Steve Chamberlain <sac@cygnus.com>
;; Optimizations by Toshiyasu Morita <toshiyasu.morita@renesas.com>
 
/* Copyright (C) 1994, 2000, 2001, 2002, 2003, 2004
Free Software Foundation, Inc.
 
This file is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the
Free Software Foundation; either version 2, or (at your option) any
later version.
 
In addition to the permissions in the GNU General Public License, the
Free Software Foundation gives you unlimited permission to link the
compiled version of this file into combinations with other programs,
and to distribute those combinations without any restriction coming
from the use of this file. (The General Public License restrictions
do apply in other respects; for example, they cover modification of
the file, and distribution when not linked into a combine
executable.)
 
This file is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
 
You should have received a copy of the GNU General Public License
along with this program; see the file COPYING. If not, write to
the Free Software Foundation, 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA. */
 
/* Assembler register definitions. */
 
#define A0 r0
#define A0L r0l
#define A0H r0h
 
#define A1 r1
#define A1L r1l
#define A1H r1h
 
#define A2 r2
#define A2L r2l
#define A2H r2h
 
#define A3 r3
#define A3L r3l
#define A3H r3h
 
#define S0 r4
#define S0L r4l
#define S0H r4h
 
#define S1 r5
#define S1L r5l
#define S1H r5h
 
#define S2 r6
#define S2L r6l
#define S2H r6h
 
#ifdef __H8300__
#define PUSHP push
#define POPP pop
 
#define A0P r0
#define A1P r1
#define A2P r2
#define A3P r3
#define S0P r4
#define S1P r5
#define S2P r6
#endif
 
#if defined (__H8300H__) || defined (__H8300S__) || defined (__H8300SX__)
#define PUSHP push.l
#define POPP pop.l
 
#define A0P er0
#define A1P er1
#define A2P er2
#define A3P er3
#define S0P er4
#define S1P er5
#define S2P er6
 
#define A0E e0
#define A1E e1
#define A2E e2
#define A3E e3
#endif
 
#ifdef __H8300H__
#ifdef __NORMAL_MODE__
.h8300hn
#else
.h8300h
#endif
#endif
 
#ifdef __H8300S__
#ifdef __NORMAL_MODE__
.h8300sn
#else
.h8300s
#endif
#endif
#ifdef __H8300SX__
#ifdef __NORMAL_MODE__
.h8300sxn
#else
.h8300sx
#endif
#endif
 
#ifdef L_cmpsi2
#ifdef __H8300__
.section .text
.align 2
.global ___cmpsi2
___cmpsi2:
cmp.w A0,A2
bne .L2
cmp.w A1,A3
bne .L4
mov.w #1,A0
rts
.L2:
bgt .L5
.L3:
mov.w #2,A0
rts
.L4:
bls .L3
.L5:
sub.w A0,A0
rts
.end
#endif
#endif /* L_cmpsi2 */
 
#ifdef L_ucmpsi2
#ifdef __H8300__
.section .text
.align 2
.global ___ucmpsi2
___ucmpsi2:
cmp.w A0,A2
bne .L2
cmp.w A1,A3
bne .L4
mov.w #1,A0
rts
.L2:
bhi .L5
.L3:
mov.w #2,A0
rts
.L4:
bls .L3
.L5:
sub.w A0,A0
rts
.end
#endif
#endif /* L_ucmpsi2 */
 
#ifdef L_divhi3
 
;; HImode divides for the H8/300.
;; We bunch all of this into one object file since there are several
;; "supporting routines".
 
; general purpose normalize routine
;
; divisor in A0
; dividend in A1
; turns both into +ve numbers, and leaves what the answer sign
; should be in A2L
 
#ifdef __H8300__
.section .text
.align 2
divnorm:
or A0H,A0H ; is divisor > 0
stc ccr,A2L
bge _lab1
not A0H ; no - then make it +ve
not A0L
adds #1,A0
_lab1: or A1H,A1H ; look at dividend
bge _lab2
not A1H ; it is -ve, make it positive
not A1L
adds #1,A1
xor #0x8,A2L; and toggle sign of result
_lab2: rts
;; Basically the same, except that the sign of the divisor determines
;; the sign.
modnorm:
or A0H,A0H ; is divisor > 0
stc ccr,A2L
bge _lab7
not A0H ; no - then make it +ve
not A0L
adds #1,A0
_lab7: or A1H,A1H ; look at dividend
bge _lab8
not A1H ; it is -ve, make it positive
not A1L
adds #1,A1
_lab8: rts
 
; A0=A0/A1 signed
 
.global ___divhi3
___divhi3:
bsr divnorm
bsr ___udivhi3
negans: btst #3,A2L ; should answer be negative ?
beq _lab4
not A0H ; yes, so make it so
not A0L
adds #1,A0
_lab4: rts
 
; A0=A0%A1 signed
 
.global ___modhi3
___modhi3:
bsr modnorm
bsr ___udivhi3
mov A3,A0
bra negans
 
; A0=A0%A1 unsigned
 
.global ___umodhi3
___umodhi3:
bsr ___udivhi3
mov A3,A0
rts
 
; A0=A0/A1 unsigned
; A3=A0%A1 unsigned
; A2H trashed
; D high 8 bits of denom
; d low 8 bits of denom
; N high 8 bits of num
; n low 8 bits of num
; M high 8 bits of mod
; m low 8 bits of mod
; Q high 8 bits of quot
; q low 8 bits of quot
; P preserve
 
; The H8/300 only has a 16/8 bit divide, so we look at the incoming and
; see how to partition up the expression.
 
.global ___udivhi3
___udivhi3:
; A0 A1 A2 A3
; Nn Dd P
sub.w A3,A3 ; Nn Dd xP 00
or A1H,A1H
bne divlongway
or A0H,A0H
beq _lab6
 
; we know that D == 0 and N is != 0
mov.b A0H,A3L ; Nn Dd xP 0N
divxu A1L,A3 ; MQ
mov.b A3L,A0H ; Q
; dealt with N, do n
_lab6: mov.b A0L,A3L ; n
divxu A1L,A3 ; mq
mov.b A3L,A0L ; Qq
mov.b A3H,A3L ; m
mov.b #0x0,A3H ; Qq 0m
rts
 
; D != 0 - which means the denominator is
; loop around to get the result.
 
divlongway:
mov.b A0H,A3L ; Nn Dd xP 0N
mov.b #0x0,A0H ; high byte of answer has to be zero
mov.b #0x8,A2H ; 8
div8: add.b A0L,A0L ; n*=2
rotxl A3L ; Make remainder bigger
rotxl A3H
sub.w A1,A3 ; Q-=N
bhs setbit ; set a bit ?
add.w A1,A3 ; no : too far , Q+=N
 
dec A2H
bne div8 ; next bit
rts
 
setbit: inc A0L ; do insert bit
dec A2H
bne div8 ; next bit
rts
 
#endif /* __H8300__ */
#endif /* L_divhi3 */
 
#ifdef L_divsi3
 
;; 4 byte integer divides for the H8/300.
;;
;; We have one routine which does all the work and lots of
;; little ones which prepare the args and massage the sign.
;; We bunch all of this into one object file since there are several
;; "supporting routines".
 
.section .text
.align 2
 
; Put abs SIs into r0/r1 and r2/r3, and leave a 1 in r6l with sign of rest.
; This function is here to keep branch displacements small.
 
#ifdef __H8300__
 
divnorm:
mov.b A0H,A0H ; is the numerator -ve
stc ccr,S2L ; keep the sign in bit 3 of S2L
bge postive
 
; negate arg
not A0H
not A1H
not A0L
not A1L
 
add #1,A1L
addx #0,A1H
addx #0,A0L
addx #0,A0H
postive:
mov.b A2H,A2H ; is the denominator -ve
bge postive2
not A2L
not A2H
not A3L
not A3H
add.b #1,A3L
addx #0,A3H
addx #0,A2L
addx #0,A2H
xor.b #0x08,S2L ; toggle the result sign
postive2:
rts
 
;; Basically the same, except that the sign of the divisor determines
;; the sign.
modnorm:
mov.b A0H,A0H ; is the numerator -ve
stc ccr,S2L ; keep the sign in bit 3 of S2L
bge mpostive
 
; negate arg
not A0H
not A1H
not A0L
not A1L
 
add #1,A1L
addx #0,A1H
addx #0,A0L
addx #0,A0H
mpostive:
mov.b A2H,A2H ; is the denominator -ve
bge mpostive2
not A2L
not A2H
not A3L
not A3H
add.b #1,A3L
addx #0,A3H
addx #0,A2L
addx #0,A2H
mpostive2:
rts
 
#else /* __H8300H__ */
 
divnorm:
mov.l A0P,A0P ; is the numerator -ve
stc ccr,S2L ; keep the sign in bit 3 of S2L
bge postive
 
neg.l A0P ; negate arg
 
postive:
mov.l A1P,A1P ; is the denominator -ve
bge postive2
 
neg.l A1P ; negate arg
xor.b #0x08,S2L ; toggle the result sign
 
postive2:
rts
 
;; Basically the same, except that the sign of the divisor determines
;; the sign.
modnorm:
mov.l A0P,A0P ; is the numerator -ve
stc ccr,S2L ; keep the sign in bit 3 of S2L
bge mpostive
 
neg.l A0P ; negate arg
 
mpostive:
mov.l A1P,A1P ; is the denominator -ve
bge mpostive2
 
neg.l A1P ; negate arg
 
mpostive2:
rts
 
#endif
 
; numerator in A0/A1
; denominator in A2/A3
.global ___modsi3
___modsi3:
#ifdef __H8300__
PUSHP S2P
PUSHP S0P
PUSHP S1P
bsr modnorm
bsr divmodsi4
mov S0,A0
mov S1,A1
bra exitdiv
#else
PUSHP S2P
bsr modnorm
bsr ___udivsi3
mov.l er3,er0
bra exitdiv
#endif
 
;; H8/300H and H8S version of ___udivsi3 is defined later in
;; the file.
#ifdef __H8300__
.global ___udivsi3
___udivsi3:
PUSHP S2P
PUSHP S0P
PUSHP S1P
bsr divmodsi4
bra reti
#endif
 
.global ___umodsi3
___umodsi3:
#ifdef __H8300__
PUSHP S2P
PUSHP S0P
PUSHP S1P
bsr divmodsi4
mov S0,A0
mov S1,A1
bra reti
#else
bsr ___udivsi3
mov.l er3,er0
rts
#endif
 
.global ___divsi3
___divsi3:
#ifdef __H8300__
PUSHP S2P
PUSHP S0P
PUSHP S1P
jsr divnorm
jsr divmodsi4
#else
PUSHP S2P
jsr divnorm
bsr ___udivsi3
#endif
 
; examine what the sign should be
exitdiv:
btst #3,S2L
beq reti
 
; should be -ve
#ifdef __H8300__
not A0H
not A1H
not A0L
not A1L
 
add #1,A1L
addx #0,A1H
addx #0,A0L
addx #0,A0H
#else /* __H8300H__ */
neg.l A0P
#endif
 
reti:
#ifdef __H8300__
POPP S1P
POPP S0P
#endif
POPP S2P
rts
 
; takes A0/A1 numerator (A0P for H8/300H)
; A2/A3 denominator (A1P for H8/300H)
; returns A0/A1 quotient (A0P for H8/300H)
; S0/S1 remainder (S0P for H8/300H)
; trashes S2H
 
#ifdef __H8300__
 
divmodsi4:
sub.w S0,S0 ; zero play area
mov.w S0,S1
mov.b A2H,S2H
or A2L,S2H
or A3H,S2H
bne DenHighNonZero
mov.b A0H,A0H
bne NumByte0Zero
mov.b A0L,A0L
bne NumByte1Zero
mov.b A1H,A1H
bne NumByte2Zero
bra NumByte3Zero
NumByte0Zero:
mov.b A0H,S1L
divxu A3L,S1
mov.b S1L,A0H
NumByte1Zero:
mov.b A0L,S1L
divxu A3L,S1
mov.b S1L,A0L
NumByte2Zero:
mov.b A1H,S1L
divxu A3L,S1
mov.b S1L,A1H
NumByte3Zero:
mov.b A1L,S1L
divxu A3L,S1
mov.b S1L,A1L
 
mov.b S1H,S1L
mov.b #0x0,S1H
rts
 
; have to do the divide by shift and test
DenHighNonZero:
mov.b A0H,S1L
mov.b A0L,A0H
mov.b A1H,A0L
mov.b A1L,A1H
 
mov.b #0,A1L
mov.b #24,S2H ; only do 24 iterations
 
nextbit:
add.w A1,A1 ; double the answer guess
rotxl A0L
rotxl A0H
 
rotxl S1L ; double remainder
rotxl S1H
rotxl S0L
rotxl S0H
sub.w A3,S1 ; does it all fit
subx A2L,S0L
subx A2H,S0H
bhs setone
 
add.w A3,S1 ; no, restore mistake
addx A2L,S0L
addx A2H,S0H
 
dec S2H
bne nextbit
rts
 
setone:
inc A1L
dec S2H
bne nextbit
rts
 
#else /* __H8300H__ */
 
;; This function also computes the remainder and stores it in er3.
.global ___udivsi3
___udivsi3:
mov.w A1E,A1E ; denominator top word 0?
bne DenHighNonZero
 
; do it the easy way, see page 107 in manual
mov.w A0E,A2
extu.l A2P
divxu.w A1,A2P
mov.w A2E,A0E
divxu.w A1,A0P
mov.w A0E,A3
mov.w A2,A0E
extu.l A3P
rts
 
; er0 = er0 / er1
; er3 = er0 % er1
; trashes er1 er2
; expects er1 >= 2^16
DenHighNonZero:
mov.l er0,er3
mov.l er1,er2
#ifdef __H8300H__
divmod_L21:
shlr.l er0
shlr.l er2 ; make divisor < 2^16
mov.w e2,e2
bne divmod_L21
#else
shlr.l #2,er2 ; make divisor < 2^16
mov.w e2,e2
beq divmod_L22A
divmod_L21:
shlr.l #2,er0
divmod_L22:
shlr.l #2,er2 ; make divisor < 2^16
mov.w e2,e2
bne divmod_L21
divmod_L22A:
rotxl.w r2
bcs divmod_L23
shlr.l er0
bra divmod_L24
divmod_L23:
rotxr.w r2
shlr.l #2,er0
divmod_L24:
#endif
;; At this point,
;; er0 contains shifted dividend
;; er1 contains divisor
;; er2 contains shifted divisor
;; er3 contains dividend, later remainder
divxu.w r2,er0 ; r0 now contains the approximate quotient (AQ)
extu.l er0
beq divmod_L25
subs #1,er0 ; er0 = AQ - 1
mov.w e1,r2
mulxu.w r0,er2 ; er2 = upper (AQ - 1) * divisor
sub.w r2,e3 ; dividend - 65536 * er2
mov.w r1,r2
mulxu.w r0,er2 ; compute er3 = remainder (tentative)
sub.l er2,er3 ; er3 = dividend - (AQ - 1) * divisor
divmod_L25:
cmp.l er1,er3 ; is divisor < remainder?
blo divmod_L26
adds #1,er0
sub.l er1,er3 ; correct the remainder
divmod_L26:
rts
 
#endif
#endif /* L_divsi3 */
 
#ifdef L_mulhi3
 
;; HImode multiply.
; The H8/300 only has an 8*8->16 multiply.
; The answer is the same as:
;
; product = (srca.l * srcb.l) + ((srca.h * srcb.l) + (srcb.h * srca.l)) * 256
; (we can ignore A1.h * A0.h cause that will all off the top)
; A0 in
; A1 in
; A0 answer
 
#ifdef __H8300__
.section .text
.align 2
.global ___mulhi3
___mulhi3:
mov.b A1L,A2L ; A2l gets srcb.l
mulxu A0L,A2 ; A2 gets first sub product
 
mov.b A0H,A3L ; prepare for
mulxu A1L,A3 ; second sub product
 
add.b A3L,A2H ; sum first two terms
 
mov.b A1H,A3L ; third sub product
mulxu A0L,A3
 
add.b A3L,A2H ; almost there
mov.w A2,A0 ; that is
rts
 
#endif
#endif /* L_mulhi3 */
 
#ifdef L_mulsi3
 
;; SImode multiply.
;;
;; I think that shift and add may be sufficient for this. Using the
;; supplied 8x8->16 would need 10 ops of 14 cycles each + overhead. This way
;; the inner loop uses maybe 20 cycles + overhead, but terminates
;; quickly on small args.
;;
;; A0/A1 src_a
;; A2/A3 src_b
;;
;; while (a)
;; {
;; if (a & 1)
;; r += b;
;; a >>= 1;
;; b <<= 1;
;; }
 
.section .text
.align 2
 
#ifdef __H8300__
 
.global ___mulsi3
___mulsi3:
PUSHP S0P
PUSHP S1P
 
sub.w S0,S0
sub.w S1,S1
 
; while (a)
_top: mov.w A0,A0
bne _more
mov.w A1,A1
beq _done
_more: ; if (a & 1)
bld #0,A1L
bcc _nobit
; r += b
add.w A3,S1
addx A2L,S0L
addx A2H,S0H
_nobit:
; a >>= 1
shlr A0H
rotxr A0L
rotxr A1H
rotxr A1L
 
; b <<= 1
add.w A3,A3
addx A2L,A2L
addx A2H,A2H
bra _top
 
_done:
mov.w S0,A0
mov.w S1,A1
POPP S1P
POPP S0P
rts
 
#else /* __H8300H__ */
 
;
; mulsi3 for H8/300H - based on Renesas SH implementation
;
; by Toshiyasu Morita
;
; Old code:
;
; 16b * 16b = 372 states (worst case)
; 32b * 32b = 724 states (worst case)
;
; New code:
;
; 16b * 16b = 48 states
; 16b * 32b = 72 states
; 32b * 32b = 92 states
;
 
.global ___mulsi3
___mulsi3:
mov.w r1,r2 ; ( 2 states) b * d
mulxu r0,er2 ; (22 states)
 
mov.w e0,r3 ; ( 2 states) a * d
beq L_skip1 ; ( 4 states)
mulxu r1,er3 ; (22 states)
add.w r3,e2 ; ( 2 states)
 
L_skip1:
mov.w e1,r3 ; ( 2 states) c * b
beq L_skip2 ; ( 4 states)
mulxu r0,er3 ; (22 states)
add.w r3,e2 ; ( 2 states)
 
L_skip2:
mov.l er2,er0 ; ( 2 states)
rts ; (10 states)
 
#endif
#endif /* L_mulsi3 */
#ifdef L_fixunssfsi_asm
/* For the h8300 we use asm to save some bytes, to
allow more programs to fit into the tiny address
space. For the H8/300H and H8S, the C version is good enough. */
#ifdef __H8300__
/* We still treat NANs different than libgcc2.c, but then, the
behavior is undefined anyways. */
.global ___fixunssfsi
___fixunssfsi:
cmp.b #0x4f,r0h
bge Large_num
jmp @___fixsfsi
Large_num:
bhi L_huge_num
xor.b #0x80,A0L
bmi L_shift8
L_huge_num:
mov.w #65535,A0
mov.w A0,A1
rts
L_shift8:
mov.b A0L,A0H
mov.b A1H,A0L
mov.b A1L,A1H
mov.b #0,A1L
rts
#endif
#endif /* L_fixunssfsi_asm */
/genmova.sh
0,0 → 1,163
#!/bin/sh
# Generate mova.md, a file containing patterns that can be implemented
# using the h8sx mova instruction.
 
echo ";; -*- buffer-read-only: t -*-"
echo ";; Generated automatically from genmova.sh"
 
# Loop over modes for the source operand (the index). Only 8-bit and
# 16-bit indices are allowed.
for s in QI HI; do
 
# Set $src to the operand syntax for this size of index.
case $s in
QI) src=%X1.b;;
HI) src=%T1.w;;
esac
 
# A match_operand for the source.
operand="(match_operand:$s 1 \"h8300_dst_operand\" \"0,rQ\")"
 
# Loop over the destination register's mode. The QI and HI versions use
# the same instructions as the SI ones, they just ignore the upper bits
# of the result.
for d in QI HI SI; do
 
# If the destination is larger than the source, include a
# zero_extend/plus pattern. We could also match zero extensions
# of memory without the plus, but it's not any smaller or faster
# than separate insns.
case $d:$s in
SI:QI | SI:HI | HI:QI)
cat <<EOF
(define_insn ""
[(set (match_operand:$d 0 "register_operand" "=r,r")
(plus:$d (zero_extend:$d $operand)
(match_operand:$d 2 "immediate_operand" "i,i")))]
"TARGET_H8300SX"
"mova/b.l @(%o2,$src),%S0"
[(set_attr "length_table" "mova")
(set_attr "cc" "none")])
 
EOF
;;
esac
 
# Loop over the shift amount.
for shift in 1 2; do
case $shift in
1) opsize=w mult=2;;
2) opsize=l mult=4;;
esac
 
# Calculate the mask of bits that will be nonzero after the source
# has been extended and shifted.
case $s:$shift in
QI:1) mask=510;;
QI:2) mask=1020;;
HI:1) mask=131070;;
HI:2) mask=262140;;
esac
 
# There doesn't seem to be a well-established canonical form for
# some of the patterns we need. Emit both shift and multiplication
# patterns.
for form in mult ashift; do
case $form in
mult) amount=$mult;;
ashift) amount=$shift;;
esac
 
case $d:$s in
# If the source and destination are the same size, we can treat
# mova as a sort of multiply-add instruction.
QI:QI | HI:HI)
cat <<EOF
(define_insn ""
[(set (match_operand:$d 0 "register_operand" "=r,r")
(plus:$d ($form:$d $operand
(const_int $amount))
(match_operand:$d 2 "immediate_operand" "i,i")))]
"TARGET_H8300SX"
"mova/$opsize.l @(%o2,$src),%S0"
[(set_attr "length_table" "mova")
(set_attr "cc" "none")])
 
EOF
;;
 
# Handle the cases where the source is smaller than the
# destination. Sometimes combine will keep the extension,
# sometimes it will use an AND.
SI:QI | SI:HI | HI:QI)
 
# Emit the forms that use zero_extend.
cat <<EOF
(define_insn ""
[(set (match_operand:$d 0 "register_operand" "=r,r")
($form:$d (zero_extend:$d $operand)
(const_int $amount)))]
"TARGET_H8300SX"
"mova/$opsize.l @(0,$src),%S0"
[(set_attr "length_table" "mova_zero")
(set_attr "cc" "none")])
 
(define_insn ""
[(set (match_operand:$d 0 "register_operand" "=r,r")
(plus:$d ($form:$d (zero_extend:$d $operand)
(const_int $amount))
(match_operand:$d 2 "immediate_operand" "i,i")))]
"TARGET_H8300SX"
"mova/$opsize.l @(%o2,$src),%S0"
[(set_attr "length_table" "mova")
(set_attr "cc" "none")])
 
EOF
 
# Now emit the forms that use AND. When the index is a register,
# these forms are effectively $d-mode operations: the index will
# be a $d-mode REG or SUBREG. When the index is a memory
# location, we will have a paradoxical subreg such as:
#
# (and:SI (mult:SI (subreg:SI (mem:QI ...) 0)
# (const_int 4))
# (const_int 1020))
#
# Match the two case separately: a $d-mode register_operand
# or a $d-mode subreg of an $s-mode memory_operand. Match the
# memory form first since register_operand accepts mem subregs
# before reload.
memory="(match_operand:$s 1 \"memory_operand\" \"m\")"
memory="(subreg:$d $memory 0)"
register="(match_operand:$d 1 \"register_operand\" \"0\")"
for paradoxical in "$memory" "$register"; do
cat <<EOF
(define_insn ""
[(set (match_operand:$d 0 "register_operand" "=r")
(and:$d ($form:$d $paradoxical
(const_int $amount))
(const_int $mask)))]
"TARGET_H8300SX"
"mova/$opsize.l @(0,$src),%S0"
[(set_attr "length_table" "mova_zero")
(set_attr "cc" "none")])
 
(define_insn ""
[(set (match_operand:$d 0 "register_operand" "=r")
(plus:$d (and:$d ($form:$d $paradoxical
(const_int $amount))
(const_int $mask))
(match_operand:$d 2 "immediate_operand" "i")))]
"TARGET_H8300SX"
"mova/$opsize.l @(%o2,$src),%S0"
[(set_attr "length_table" "mova")
(set_attr "cc" "none")])
 
EOF
done
;;
esac
done
done
done
done
/t-elf
0,0 → 1,6
EXTRA_MULTILIB_PARTS= crti.o crtn.o crtbegin.o crtend.o
 
$(T)crti.o: $(srcdir)/config/h8300/crti.asm $(GCC_PASSES)
$(GCC_FOR_TARGET) $(MULTILIB_CFLAGS) -c -o $(T)crti.o -x assembler-with-cpp $(srcdir)/config/h8300/crti.asm
$(T)crtn.o: $(srcdir)/config/h8300/crtn.asm $(GCC_PASSES)
$(GCC_FOR_TARGET) $(MULTILIB_CFLAGS) -c -o $(T)crtn.o -x assembler-with-cpp $(srcdir)/config/h8300/crtn.asm
/h8300-protos.h
0,0 → 1,122
/* Definitions of target machine for GNU compiler.
Renesas H8/300 version
Copyright (C) 2000, 2002, 2003, 2004, 2005, 2007 Free Software Foundation, Inc.
Contributed by Steve Chamberlain (sac@cygnus.com),
Jim Wilson (wilson@cygnus.com), and Doug Evans (dje@cygnus.com).
 
This file is part of GCC.
 
GCC is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3, or (at your option)
any later version.
 
GCC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
 
You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING3. If not see
<http://www.gnu.org/licenses/>. */
 
#ifndef GCC_H8300_PROTOS_H
#define GCC_H8300_PROTOS_H
 
/* Declarations for functions used in insn-output.c. */
#ifdef RTX_CODE
extern unsigned int compute_mov_length (rtx *);
extern const char *output_plussi (rtx *);
extern unsigned int compute_plussi_length (rtx *);
extern int compute_plussi_cc (rtx *);
extern const char *output_a_shift (rtx *);
extern unsigned int compute_a_shift_length (rtx, rtx *);
extern int compute_a_shift_cc (rtx, rtx *);
extern const char *output_a_rotate (enum rtx_code, rtx *);
extern unsigned int compute_a_rotate_length (rtx *);
extern const char *output_simode_bld (int, rtx[]);
extern void print_operand_address (FILE *, rtx);
extern void print_operand (FILE *, rtx, int);
extern void final_prescan_insn (rtx, rtx *, int);
extern int h8300_expand_movsi (rtx[]);
extern void notice_update_cc (rtx, rtx);
extern const char *output_logical_op (enum machine_mode, rtx *);
extern unsigned int compute_logical_op_length (enum machine_mode,
rtx *);
extern int compute_logical_op_cc (enum machine_mode, rtx *);
extern void h8300_expand_branch (enum rtx_code, rtx);
extern bool expand_a_shift (enum machine_mode, int, rtx[]);
extern int h8300_shift_needs_scratch_p (int, enum machine_mode);
extern int expand_a_rotate (rtx[]);
extern int fix_bit_operand (rtx *, enum rtx_code);
extern int h8300_adjust_insn_length (rtx, int);
extern void split_adds_subs (enum machine_mode, rtx[]);
 
extern int h8300_eightbit_constant_address_p (rtx);
extern int h8300_tiny_constant_address_p (rtx);
extern int byte_accesses_mergeable_p (rtx, rtx);
extern int same_cmp_preceding_p (rtx);
extern int same_cmp_following_p (rtx);
 
extern int h8300_legitimate_constant_p (rtx);
extern int h8300_legitimate_address_p (enum machine_mode, rtx, int);
 
/* Used in builtins.c */
extern rtx h8300_return_addr_rtx (int, rtx);
 
/* Classifies an h8sx shift operation.
 
H8SX_SHIFT_NONE
The shift cannot be done in a single instruction.
 
H8SX_SHIFT_UNARY
The shift is effectively a unary operation. The instruction will
allow any sort of destination operand and have a format similar
to neg and not. This is true of certain power-of-2 shifts.
 
H8SX_SHIFT_BINARY
The shift is a binary operation. The destination must be a
register and the source can be a register or a constant. */
enum h8sx_shift_type {
H8SX_SHIFT_NONE,
H8SX_SHIFT_UNARY,
H8SX_SHIFT_BINARY
};
 
extern enum h8sx_shift_type h8sx_classify_shift (enum machine_mode, enum rtx_code, rtx);
extern int h8300_ldm_stm_parallel (rtvec, int, int);
#endif /* RTX_CODE */
 
#ifdef TREE_CODE
extern struct rtx_def *function_arg (CUMULATIVE_ARGS *,
enum machine_mode, tree, int);
extern int h8300_funcvec_function_p (tree);
extern int h8300_eightbit_data_p (tree);
extern int h8300_tiny_data_p (tree);
#endif /* TREE_CODE */
 
extern void h8300_init_once (void);
extern int h8300_can_use_return_insn_p (void);
extern void h8300_expand_prologue (void);
extern void h8300_expand_epilogue (void);
extern int h8300_current_function_interrupt_function_p (void);
extern int h8300_initial_elimination_offset (int, int);
extern int h8300_regs_ok_for_stm (int, rtx[]);
extern int h8300_hard_regno_rename_ok (unsigned int, unsigned int);
extern int h8300_hard_regno_nregs (int, enum machine_mode);
extern int h8300_hard_regno_mode_ok (int, enum machine_mode);
 
struct cpp_reader;
extern void h8300_pr_interrupt (struct cpp_reader *);
extern void h8300_pr_saveall (struct cpp_reader *);
extern enum reg_class h8300_reg_class_from_letter (int);
extern rtx h8300_get_index (rtx, enum machine_mode mode, int *);
extern unsigned int h8300_insn_length_from_table (rtx, rtx *);
extern const char * output_h8sx_shift (rtx *, int, int);
extern bool h8300_operands_match_p (rtx *);
extern bool h8sx_mergeable_memrefs_p (rtx, rtx);
extern bool h8sx_emit_movmd (rtx, rtx, rtx, HOST_WIDE_INT);
extern void h8300_swap_into_er6 (rtx);
extern void h8300_swap_out_of_er6 (rtx);
 
#endif /* ! GCC_H8300_PROTOS_H */
/clzhi2.c
0,0 → 1,40
/* The implementation of __clzhi2.
Copyright (C) 2003 Free Software Foundation, Inc.
 
This file is part of GCC.
 
GCC is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
 
In addition to the permissions in the GNU General Public License, the
Free Software Foundation gives you unlimited permission to link the
compiled version of this file into combinations with other programs,
and to distribute those combinations without any restriction coming
from the use of this file. (The General Public License restrictions
do apply in other respects; for example, they cover modification of
the file, and distribution when not linked into a combine
executable.)
 
GCC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
 
You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING. If not, write to
the Free Software Foundation, 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA. */
 
int __clzhi2 (unsigned short x);
 
int
__clzhi2 (unsigned short x)
{
int i;
for (i = 0; i < 16; i++)
if (x & ((unsigned short) 1 << (15 - i)))
break;
return i;
}

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.