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

Subversion Repositories openrisc

Compare Revisions

  • This comparison shows the changes necessary to convert path
    /openrisc/trunk/gnu-old/gcc-4.2.2/gcc/config/mn10300
    from Rev 154 to Rev 816
    Reverse comparison

Rev 154 → Rev 816

/mn10300.c
0,0 → 1,2128
/* Subroutines for insn-output.c for Matsushita MN10300 series
Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
2007 Free Software Foundation, Inc.
Contributed by Jeff Law (law@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 "optabs.h"
#include "function.h"
#include "obstack.h"
#include "toplev.h"
#include "tm_p.h"
#include "target.h"
#include "target-def.h"
 
/* This is used by GOTaddr2picreg to uniquely identify
UNSPEC_INT_LABELs. */
int mn10300_unspec_int_label_counter;
 
/* This is used in the am33_2.0-linux-gnu port, in which global symbol
names are not prefixed by underscores, to tell whether to prefix a
label with a plus sign or not, so that the assembler can tell
symbol names from register names. */
int mn10300_protect_label;
 
/* The selected processor. */
enum processor_type mn10300_processor = PROCESSOR_DEFAULT;
 
/* The size of the callee register save area. Right now we save everything
on entry since it costs us nothing in code size. It does cost us from a
speed standpoint, so we want to optimize this sooner or later. */
#define REG_SAVE_BYTES (4 * regs_ever_live[2] \
+ 4 * regs_ever_live[3] \
+ 4 * regs_ever_live[6] \
+ 4 * regs_ever_live[7] \
+ 16 * (regs_ever_live[14] || regs_ever_live[15] \
|| regs_ever_live[16] || regs_ever_live[17]))
 
 
static bool mn10300_handle_option (size_t, const char *, int);
static int mn10300_address_cost_1 (rtx, int *);
static int mn10300_address_cost (rtx);
static bool mn10300_rtx_costs (rtx, int, int, int *);
static void mn10300_file_start (void);
static bool mn10300_return_in_memory (tree, tree);
static rtx mn10300_builtin_saveregs (void);
static bool mn10300_pass_by_reference (CUMULATIVE_ARGS *, enum machine_mode,
tree, bool);
static int mn10300_arg_partial_bytes (CUMULATIVE_ARGS *, enum machine_mode,
tree, bool);
/* Initialize the GCC target structure. */
#undef TARGET_ASM_ALIGNED_HI_OP
#define TARGET_ASM_ALIGNED_HI_OP "\t.hword\t"
 
#undef TARGET_RTX_COSTS
#define TARGET_RTX_COSTS mn10300_rtx_costs
#undef TARGET_ADDRESS_COST
#define TARGET_ADDRESS_COST mn10300_address_cost
 
#undef TARGET_ASM_FILE_START
#define TARGET_ASM_FILE_START mn10300_file_start
#undef TARGET_ASM_FILE_START_FILE_DIRECTIVE
#define TARGET_ASM_FILE_START_FILE_DIRECTIVE true
 
#undef TARGET_DEFAULT_TARGET_FLAGS
#define TARGET_DEFAULT_TARGET_FLAGS MASK_MULT_BUG | MASK_PTR_A0D0
#undef TARGET_HANDLE_OPTION
#define TARGET_HANDLE_OPTION mn10300_handle_option
 
#undef TARGET_ENCODE_SECTION_INFO
#define TARGET_ENCODE_SECTION_INFO mn10300_encode_section_info
 
#undef TARGET_PROMOTE_PROTOTYPES
#define TARGET_PROMOTE_PROTOTYPES hook_bool_tree_true
#undef TARGET_RETURN_IN_MEMORY
#define TARGET_RETURN_IN_MEMORY mn10300_return_in_memory
#undef TARGET_PASS_BY_REFERENCE
#define TARGET_PASS_BY_REFERENCE mn10300_pass_by_reference
#undef TARGET_CALLEE_COPIES
#define TARGET_CALLEE_COPIES hook_bool_CUMULATIVE_ARGS_mode_tree_bool_true
#undef TARGET_ARG_PARTIAL_BYTES
#define TARGET_ARG_PARTIAL_BYTES mn10300_arg_partial_bytes
 
#undef TARGET_EXPAND_BUILTIN_SAVEREGS
#define TARGET_EXPAND_BUILTIN_SAVEREGS mn10300_builtin_saveregs
 
static void mn10300_encode_section_info (tree, rtx, int);
struct gcc_target targetm = TARGET_INITIALIZER;
/* Implement TARGET_HANDLE_OPTION. */
 
static bool
mn10300_handle_option (size_t code,
const char *arg ATTRIBUTE_UNUSED,
int value)
{
switch (code)
{
case OPT_mam33:
mn10300_processor = value ? PROCESSOR_AM33 : PROCESSOR_MN10300;
return true;
case OPT_mam33_2:
mn10300_processor = (value
? PROCESSOR_AM33_2
: MIN (PROCESSOR_AM33, PROCESSOR_DEFAULT));
return true;
default:
return true;
}
}
 
/* Implement OVERRIDE_OPTIONS. */
 
void
mn10300_override_options (void)
{
if (TARGET_AM33)
target_flags &= ~MASK_MULT_BUG;
}
 
static void
mn10300_file_start (void)
{
default_file_start ();
 
if (TARGET_AM33_2)
fprintf (asm_out_file, "\t.am33_2\n");
else if (TARGET_AM33)
fprintf (asm_out_file, "\t.am33\n");
}
 
/* Print operand X using operand code CODE to assembly language output file
FILE. */
 
void
print_operand (FILE *file, rtx x, int code)
{
switch (code)
{
case 'b':
case 'B':
if (cc_status.mdep.fpCC)
{
switch (code == 'b' ? GET_CODE (x)
: reverse_condition_maybe_unordered (GET_CODE (x)))
{
case NE:
fprintf (file, "ne");
break;
case EQ:
fprintf (file, "eq");
break;
case GE:
fprintf (file, "ge");
break;
case GT:
fprintf (file, "gt");
break;
case LE:
fprintf (file, "le");
break;
case LT:
fprintf (file, "lt");
break;
case ORDERED:
fprintf (file, "lge");
break;
case UNORDERED:
fprintf (file, "uo");
break;
case LTGT:
fprintf (file, "lg");
break;
case UNEQ:
fprintf (file, "ue");
break;
case UNGE:
fprintf (file, "uge");
break;
case UNGT:
fprintf (file, "ug");
break;
case UNLE:
fprintf (file, "ule");
break;
case UNLT:
fprintf (file, "ul");
break;
default:
gcc_unreachable ();
}
break;
}
/* These are normal and reversed branches. */
switch (code == 'b' ? GET_CODE (x) : reverse_condition (GET_CODE (x)))
{
case NE:
fprintf (file, "ne");
break;
case EQ:
fprintf (file, "eq");
break;
case GE:
fprintf (file, "ge");
break;
case GT:
fprintf (file, "gt");
break;
case LE:
fprintf (file, "le");
break;
case LT:
fprintf (file, "lt");
break;
case GEU:
fprintf (file, "cc");
break;
case GTU:
fprintf (file, "hi");
break;
case LEU:
fprintf (file, "ls");
break;
case LTU:
fprintf (file, "cs");
break;
default:
gcc_unreachable ();
}
break;
case 'C':
/* This is used for the operand to a call instruction;
if it's a REG, enclose it in parens, else output
the operand normally. */
if (GET_CODE (x) == REG)
{
fputc ('(', file);
print_operand (file, x, 0);
fputc (')', file);
}
else
print_operand (file, x, 0);
break;
case 'D':
switch (GET_CODE (x))
{
case MEM:
fputc ('(', file);
output_address (XEXP (x, 0));
fputc (')', file);
break;
 
case REG:
fprintf (file, "fd%d", REGNO (x) - 18);
break;
 
default:
gcc_unreachable ();
}
break;
 
/* These are the least significant word in a 64bit value. */
case 'L':
switch (GET_CODE (x))
{
case MEM:
fputc ('(', file);
output_address (XEXP (x, 0));
fputc (')', file);
break;
 
case REG:
fprintf (file, "%s", reg_names[REGNO (x)]);
break;
 
case SUBREG:
fprintf (file, "%s", reg_names[subreg_regno (x)]);
break;
 
case CONST_DOUBLE:
{
long val[2];
REAL_VALUE_TYPE rv;
 
switch (GET_MODE (x))
{
case DFmode:
REAL_VALUE_FROM_CONST_DOUBLE (rv, x);
REAL_VALUE_TO_TARGET_DOUBLE (rv, val);
fprintf (file, "0x%lx", val[0]);
break;;
case SFmode:
REAL_VALUE_FROM_CONST_DOUBLE (rv, x);
REAL_VALUE_TO_TARGET_SINGLE (rv, val[0]);
fprintf (file, "0x%lx", val[0]);
break;;
case VOIDmode:
case DImode:
print_operand_address (file,
GEN_INT (CONST_DOUBLE_LOW (x)));
break;
default:
break;
}
break;
}
 
case CONST_INT:
{
rtx low, high;
split_double (x, &low, &high);
fprintf (file, "%ld", (long)INTVAL (low));
break;
}
 
default:
gcc_unreachable ();
}
break;
 
/* Similarly, but for the most significant word. */
case 'H':
switch (GET_CODE (x))
{
case MEM:
fputc ('(', file);
x = adjust_address (x, SImode, 4);
output_address (XEXP (x, 0));
fputc (')', file);
break;
 
case REG:
fprintf (file, "%s", reg_names[REGNO (x) + 1]);
break;
 
case SUBREG:
fprintf (file, "%s", reg_names[subreg_regno (x) + 1]);
break;
 
case CONST_DOUBLE:
{
long val[2];
REAL_VALUE_TYPE rv;
 
switch (GET_MODE (x))
{
case DFmode:
REAL_VALUE_FROM_CONST_DOUBLE (rv, x);
REAL_VALUE_TO_TARGET_DOUBLE (rv, val);
fprintf (file, "0x%lx", val[1]);
break;;
case SFmode:
gcc_unreachable ();
case VOIDmode:
case DImode:
print_operand_address (file,
GEN_INT (CONST_DOUBLE_HIGH (x)));
break;
default:
break;
}
break;
}
 
case CONST_INT:
{
rtx low, high;
split_double (x, &low, &high);
fprintf (file, "%ld", (long)INTVAL (high));
break;
}
 
default:
gcc_unreachable ();
}
break;
 
case 'A':
fputc ('(', file);
if (GET_CODE (XEXP (x, 0)) == REG)
output_address (gen_rtx_PLUS (SImode, XEXP (x, 0), const0_rtx));
else
output_address (XEXP (x, 0));
fputc (')', file);
break;
 
case 'N':
gcc_assert (INTVAL (x) >= -128 && INTVAL (x) <= 255);
fprintf (file, "%d", (int)((~INTVAL (x)) & 0xff));
break;
 
case 'U':
gcc_assert (INTVAL (x) >= -128 && INTVAL (x) <= 255);
fprintf (file, "%d", (int)(INTVAL (x) & 0xff));
break;
 
/* For shift counts. The hardware ignores the upper bits of
any immediate, but the assembler will flag an out of range
shift count as an error. So we mask off the high bits
of the immediate here. */
case 'S':
if (GET_CODE (x) == CONST_INT)
{
fprintf (file, "%d", (int)(INTVAL (x) & 0x1f));
break;
}
/* FALL THROUGH */
 
default:
switch (GET_CODE (x))
{
case MEM:
fputc ('(', file);
output_address (XEXP (x, 0));
fputc (')', file);
break;
 
case PLUS:
output_address (x);
break;
 
case REG:
fprintf (file, "%s", reg_names[REGNO (x)]);
break;
 
case SUBREG:
fprintf (file, "%s", reg_names[subreg_regno (x)]);
break;
 
/* This will only be single precision.... */
case CONST_DOUBLE:
{
unsigned long val;
REAL_VALUE_TYPE rv;
 
REAL_VALUE_FROM_CONST_DOUBLE (rv, x);
REAL_VALUE_TO_TARGET_SINGLE (rv, val);
fprintf (file, "0x%lx", val);
break;
}
 
case CONST_INT:
case SYMBOL_REF:
case CONST:
case LABEL_REF:
case CODE_LABEL:
case UNSPEC:
print_operand_address (file, x);
break;
default:
gcc_unreachable ();
}
break;
}
}
 
/* Output assembly language output for the address ADDR to FILE. */
 
void
print_operand_address (FILE *file, rtx addr)
{
switch (GET_CODE (addr))
{
case POST_INC:
print_operand_address (file, XEXP (addr, 0));
fputc ('+', file);
break;
case REG:
print_operand (file, addr, 0);
break;
case PLUS:
{
rtx base, index;
if (REG_P (XEXP (addr, 0))
&& REG_OK_FOR_BASE_P (XEXP (addr, 0)))
base = XEXP (addr, 0), index = XEXP (addr, 1);
else if (REG_P (XEXP (addr, 1))
&& REG_OK_FOR_BASE_P (XEXP (addr, 1)))
base = XEXP (addr, 1), index = XEXP (addr, 0);
else
gcc_unreachable ();
print_operand (file, index, 0);
fputc (',', file);
print_operand (file, base, 0);;
break;
}
case SYMBOL_REF:
output_addr_const (file, addr);
break;
default:
output_addr_const (file, addr);
break;
}
}
 
/* Count the number of FP registers that have to be saved. */
static int
fp_regs_to_save (void)
{
int i, n = 0;
 
if (! TARGET_AM33_2)
return 0;
 
for (i = FIRST_FP_REGNUM; i <= LAST_FP_REGNUM; ++i)
if (regs_ever_live[i] && ! call_used_regs[i])
++n;
 
return n;
}
 
/* Print a set of registers in the format required by "movm" and "ret".
Register K is saved if bit K of MASK is set. The data and address
registers can be stored individually, but the extended registers cannot.
We assume that the mask alread takes that into account. For instance,
bits 14 to 17 must have the same value. */
 
void
mn10300_print_reg_list (FILE *file, int mask)
{
int need_comma;
int i;
 
need_comma = 0;
fputc ('[', file);
 
for (i = 0; i < FIRST_EXTENDED_REGNUM; i++)
if ((mask & (1 << i)) != 0)
{
if (need_comma)
fputc (',', file);
fputs (reg_names [i], file);
need_comma = 1;
}
 
if ((mask & 0x3c000) != 0)
{
gcc_assert ((mask & 0x3c000) == 0x3c000);
if (need_comma)
fputc (',', file);
fputs ("exreg1", file);
need_comma = 1;
}
 
fputc (']', file);
}
 
int
can_use_return_insn (void)
{
/* size includes the fixed stack space needed for function calls. */
int size = get_frame_size () + current_function_outgoing_args_size;
 
/* And space for the return pointer. */
size += current_function_outgoing_args_size ? 4 : 0;
 
return (reload_completed
&& size == 0
&& !regs_ever_live[2]
&& !regs_ever_live[3]
&& !regs_ever_live[6]
&& !regs_ever_live[7]
&& !regs_ever_live[14]
&& !regs_ever_live[15]
&& !regs_ever_live[16]
&& !regs_ever_live[17]
&& fp_regs_to_save () == 0
&& !frame_pointer_needed);
}
 
/* Returns the set of live, callee-saved registers as a bitmask. The
callee-saved extended registers cannot be stored individually, so
all of them will be included in the mask if any one of them is used. */
 
int
mn10300_get_live_callee_saved_regs (void)
{
int mask;
int i;
 
mask = 0;
for (i = 0; i <= LAST_EXTENDED_REGNUM; i++)
if (regs_ever_live[i] && ! call_used_regs[i])
mask |= (1 << i);
if ((mask & 0x3c000) != 0)
mask |= 0x3c000;
 
return mask;
}
 
/* Generate an instruction that pushes several registers onto the stack.
Register K will be saved if bit K in MASK is set. The function does
nothing if MASK is zero.
 
To be compatible with the "movm" instruction, the lowest-numbered
register must be stored in the lowest slot. If MASK is the set
{ R1,...,RN }, where R1...RN are ordered least first, the generated
instruction will have the form:
 
(parallel
(set (reg:SI 9) (plus:SI (reg:SI 9) (const_int -N*4)))
(set (mem:SI (plus:SI (reg:SI 9)
(const_int -1*4)))
(reg:SI RN))
...
(set (mem:SI (plus:SI (reg:SI 9)
(const_int -N*4)))
(reg:SI R1))) */
 
void
mn10300_gen_multiple_store (int mask)
{
if (mask != 0)
{
int i;
int count;
rtx par;
int pari;
 
/* Count how many registers need to be saved. */
count = 0;
for (i = 0; i <= LAST_EXTENDED_REGNUM; i++)
if ((mask & (1 << i)) != 0)
count += 1;
 
/* We need one PARALLEL element to update the stack pointer and
an additional element for each register that is stored. */
par = gen_rtx_PARALLEL (VOIDmode, rtvec_alloc (count + 1));
 
/* Create the instruction that updates the stack pointer. */
XVECEXP (par, 0, 0)
= gen_rtx_SET (SImode,
stack_pointer_rtx,
gen_rtx_PLUS (SImode,
stack_pointer_rtx,
GEN_INT (-count * 4)));
 
/* Create each store. */
pari = 1;
for (i = LAST_EXTENDED_REGNUM; i >= 0; i--)
if ((mask & (1 << i)) != 0)
{
rtx address = gen_rtx_PLUS (SImode,
stack_pointer_rtx,
GEN_INT (-pari * 4));
XVECEXP(par, 0, pari)
= gen_rtx_SET (VOIDmode,
gen_rtx_MEM (SImode, address),
gen_rtx_REG (SImode, i));
pari += 1;
}
 
par = emit_insn (par);
RTX_FRAME_RELATED_P (par) = 1;
}
}
 
void
expand_prologue (void)
{
HOST_WIDE_INT size;
 
/* SIZE includes the fixed stack space needed for function calls. */
size = get_frame_size () + current_function_outgoing_args_size;
size += (current_function_outgoing_args_size ? 4 : 0);
 
/* If we use any of the callee-saved registers, save them now. */
mn10300_gen_multiple_store (mn10300_get_live_callee_saved_regs ());
 
if (TARGET_AM33_2 && fp_regs_to_save ())
{
int num_regs_to_save = fp_regs_to_save (), i;
HOST_WIDE_INT xsize;
enum { save_sp_merge,
save_sp_no_merge,
save_sp_partial_merge,
save_a0_merge,
save_a0_no_merge } strategy;
unsigned int strategy_size = (unsigned)-1, this_strategy_size;
rtx reg;
rtx insn;
 
/* We have several different strategies to save FP registers.
We can store them using SP offsets, which is beneficial if
there are just a few registers to save, or we can use `a0' in
post-increment mode (`a0' is the only call-clobbered address
register that is never used to pass information to a
function). Furthermore, if we don't need a frame pointer, we
can merge the two SP adds into a single one, but this isn't
always beneficial; sometimes we can just split the two adds
so that we don't exceed a 16-bit constant size. The code
below will select which strategy to use, so as to generate
smallest code. Ties are broken in favor or shorter sequences
(in terms of number of instructions). */
 
#define SIZE_ADD_AX(S) ((((S) >= (1 << 15)) || ((S) < -(1 << 15))) ? 6 \
: (((S) >= (1 << 7)) || ((S) < -(1 << 7))) ? 4 : 2)
#define SIZE_ADD_SP(S) ((((S) >= (1 << 15)) || ((S) < -(1 << 15))) ? 6 \
: (((S) >= (1 << 7)) || ((S) < -(1 << 7))) ? 4 : 3)
#define SIZE_FMOV_LIMIT(S,N,L,SIZE1,SIZE2,ELSE) \
(((S) >= (L)) ? (SIZE1) * (N) \
: ((S) + 4 * (N) >= (L)) ? (((L) - (S)) / 4 * (SIZE2) \
+ ((S) + 4 * (N) - (L)) / 4 * (SIZE1)) \
: (ELSE))
#define SIZE_FMOV_SP_(S,N) \
(SIZE_FMOV_LIMIT ((S), (N), (1 << 24), 7, 6, \
SIZE_FMOV_LIMIT ((S), (N), (1 << 8), 6, 4, \
(S) ? 4 * (N) : 3 + 4 * ((N) - 1))))
#define SIZE_FMOV_SP(S,N) (SIZE_FMOV_SP_ ((unsigned HOST_WIDE_INT)(S), (N)))
 
/* Consider alternative save_sp_merge only if we don't need the
frame pointer and size is nonzero. */
if (! frame_pointer_needed && size)
{
/* Insn: add -(size + 4 * num_regs_to_save), sp. */
this_strategy_size = SIZE_ADD_SP (-(size + 4 * num_regs_to_save));
/* Insn: fmov fs#, (##, sp), for each fs# to be saved. */
this_strategy_size += SIZE_FMOV_SP (size, num_regs_to_save);
 
if (this_strategy_size < strategy_size)
{
strategy = save_sp_merge;
strategy_size = this_strategy_size;
}
}
 
/* Consider alternative save_sp_no_merge unconditionally. */
/* Insn: add -4 * num_regs_to_save, sp. */
this_strategy_size = SIZE_ADD_SP (-4 * num_regs_to_save);
/* Insn: fmov fs#, (##, sp), for each fs# to be saved. */
this_strategy_size += SIZE_FMOV_SP (0, num_regs_to_save);
if (size)
{
/* Insn: add -size, sp. */
this_strategy_size += SIZE_ADD_SP (-size);
}
 
if (this_strategy_size < strategy_size)
{
strategy = save_sp_no_merge;
strategy_size = this_strategy_size;
}
 
/* Consider alternative save_sp_partial_merge only if we don't
need a frame pointer and size is reasonably large. */
if (! frame_pointer_needed && size + 4 * num_regs_to_save > 128)
{
/* Insn: add -128, sp. */
this_strategy_size = SIZE_ADD_SP (-128);
/* Insn: fmov fs#, (##, sp), for each fs# to be saved. */
this_strategy_size += SIZE_FMOV_SP (128 - 4 * num_regs_to_save,
num_regs_to_save);
if (size)
{
/* Insn: add 128-size, sp. */
this_strategy_size += SIZE_ADD_SP (128 - size);
}
 
if (this_strategy_size < strategy_size)
{
strategy = save_sp_partial_merge;
strategy_size = this_strategy_size;
}
}
 
/* Consider alternative save_a0_merge only if we don't need a
frame pointer, size is nonzero and the user hasn't
changed the calling conventions of a0. */
if (! frame_pointer_needed && size
&& call_used_regs[FIRST_ADDRESS_REGNUM]
&& ! fixed_regs[FIRST_ADDRESS_REGNUM])
{
/* Insn: add -(size + 4 * num_regs_to_save), sp. */
this_strategy_size = SIZE_ADD_SP (-(size + 4 * num_regs_to_save));
/* Insn: mov sp, a0. */
this_strategy_size++;
if (size)
{
/* Insn: add size, a0. */
this_strategy_size += SIZE_ADD_AX (size);
}
/* Insn: fmov fs#, (a0+), for each fs# to be saved. */
this_strategy_size += 3 * num_regs_to_save;
 
if (this_strategy_size < strategy_size)
{
strategy = save_a0_merge;
strategy_size = this_strategy_size;
}
}
 
/* Consider alternative save_a0_no_merge if the user hasn't
changed the calling conventions of a0. */
if (call_used_regs[FIRST_ADDRESS_REGNUM]
&& ! fixed_regs[FIRST_ADDRESS_REGNUM])
{
/* Insn: add -4 * num_regs_to_save, sp. */
this_strategy_size = SIZE_ADD_SP (-4 * num_regs_to_save);
/* Insn: mov sp, a0. */
this_strategy_size++;
/* Insn: fmov fs#, (a0+), for each fs# to be saved. */
this_strategy_size += 3 * num_regs_to_save;
if (size)
{
/* Insn: add -size, sp. */
this_strategy_size += SIZE_ADD_SP (-size);
}
 
if (this_strategy_size < strategy_size)
{
strategy = save_a0_no_merge;
strategy_size = this_strategy_size;
}
}
 
/* Emit the initial SP add, common to all strategies. */
switch (strategy)
{
case save_sp_no_merge:
case save_a0_no_merge:
emit_insn (gen_addsi3 (stack_pointer_rtx,
stack_pointer_rtx,
GEN_INT (-4 * num_regs_to_save)));
xsize = 0;
break;
 
case save_sp_partial_merge:
emit_insn (gen_addsi3 (stack_pointer_rtx,
stack_pointer_rtx,
GEN_INT (-128)));
xsize = 128 - 4 * num_regs_to_save;
size -= xsize;
break;
 
case save_sp_merge:
case save_a0_merge:
emit_insn (gen_addsi3 (stack_pointer_rtx,
stack_pointer_rtx,
GEN_INT (-(size + 4 * num_regs_to_save))));
/* We'll have to adjust FP register saves according to the
frame size. */
xsize = size;
/* Since we've already created the stack frame, don't do it
again at the end of the function. */
size = 0;
break;
 
default:
gcc_unreachable ();
}
/* Now prepare register a0, if we have decided to use it. */
switch (strategy)
{
case save_sp_merge:
case save_sp_no_merge:
case save_sp_partial_merge:
reg = 0;
break;
 
case save_a0_merge:
case save_a0_no_merge:
reg = gen_rtx_REG (SImode, FIRST_ADDRESS_REGNUM);
emit_insn (gen_movsi (reg, stack_pointer_rtx));
if (xsize)
emit_insn (gen_addsi3 (reg, reg, GEN_INT (xsize)));
reg = gen_rtx_POST_INC (SImode, reg);
break;
default:
gcc_unreachable ();
}
/* Now actually save the FP registers. */
for (i = FIRST_FP_REGNUM; i <= LAST_FP_REGNUM; ++i)
if (regs_ever_live[i] && ! call_used_regs[i])
{
rtx addr;
 
if (reg)
addr = reg;
else
{
/* If we aren't using `a0', use an SP offset. */
if (xsize)
{
addr = gen_rtx_PLUS (SImode,
stack_pointer_rtx,
GEN_INT (xsize));
}
else
addr = stack_pointer_rtx;
xsize += 4;
}
 
insn = emit_insn (gen_movsi (gen_rtx_MEM (SImode, addr),
gen_rtx_REG (SImode, i)));
 
RTX_FRAME_RELATED_P (insn) = 1;
}
}
 
/* Now put the frame pointer into the frame pointer register. */
if (frame_pointer_needed)
emit_move_insn (frame_pointer_rtx, stack_pointer_rtx);
 
/* Allocate stack for this frame. */
if (size)
emit_insn (gen_addsi3 (stack_pointer_rtx,
stack_pointer_rtx,
GEN_INT (-size)));
if (flag_pic && regs_ever_live[PIC_OFFSET_TABLE_REGNUM])
{
rtx insn = get_last_insn ();
rtx last = emit_insn (gen_GOTaddr2picreg ());
 
/* Mark these insns as possibly dead. Sometimes, flow2 may
delete all uses of the PIC register. In this case, let it
delete the initialization too. */
do
{
insn = NEXT_INSN (insn);
 
REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_MAYBE_DEAD,
const0_rtx,
REG_NOTES (insn));
}
while (insn != last);
}
}
 
void
expand_epilogue (void)
{
HOST_WIDE_INT size;
 
/* SIZE includes the fixed stack space needed for function calls. */
size = get_frame_size () + current_function_outgoing_args_size;
size += (current_function_outgoing_args_size ? 4 : 0);
 
if (TARGET_AM33_2 && fp_regs_to_save ())
{
int num_regs_to_save = fp_regs_to_save (), i;
rtx reg = 0;
 
/* We have several options to restore FP registers. We could
load them from SP offsets, but, if there are enough FP
registers to restore, we win if we use a post-increment
addressing mode. */
 
/* If we have a frame pointer, it's the best option, because we
already know it has the value we want. */
if (frame_pointer_needed)
reg = gen_rtx_REG (SImode, FRAME_POINTER_REGNUM);
/* Otherwise, we may use `a1', since it's call-clobbered and
it's never used for return values. But only do so if it's
smaller than using SP offsets. */
else
{
enum { restore_sp_post_adjust,
restore_sp_pre_adjust,
restore_sp_partial_adjust,
restore_a1 } strategy;
unsigned int this_strategy_size, strategy_size = (unsigned)-1;
 
/* Consider using sp offsets before adjusting sp. */
/* Insn: fmov (##,sp),fs#, for each fs# to be restored. */
this_strategy_size = SIZE_FMOV_SP (size, num_regs_to_save);
/* If size is too large, we'll have to adjust SP with an
add. */
if (size + 4 * num_regs_to_save + REG_SAVE_BYTES > 255)
{
/* Insn: add size + 4 * num_regs_to_save, sp. */
this_strategy_size += SIZE_ADD_SP (size + 4 * num_regs_to_save);
}
/* If we don't have to restore any non-FP registers,
we'll be able to save one byte by using rets. */
if (! REG_SAVE_BYTES)
this_strategy_size--;
 
if (this_strategy_size < strategy_size)
{
strategy = restore_sp_post_adjust;
strategy_size = this_strategy_size;
}
 
/* Consider using sp offsets after adjusting sp. */
/* Insn: add size, sp. */
this_strategy_size = SIZE_ADD_SP (size);
/* Insn: fmov (##,sp),fs#, for each fs# to be restored. */
this_strategy_size += SIZE_FMOV_SP (0, num_regs_to_save);
/* We're going to use ret to release the FP registers
save area, so, no savings. */
 
if (this_strategy_size < strategy_size)
{
strategy = restore_sp_pre_adjust;
strategy_size = this_strategy_size;
}
 
/* Consider using sp offsets after partially adjusting sp.
When size is close to 32Kb, we may be able to adjust SP
with an imm16 add instruction while still using fmov
(d8,sp). */
if (size + 4 * num_regs_to_save + REG_SAVE_BYTES > 255)
{
/* Insn: add size + 4 * num_regs_to_save
+ REG_SAVE_BYTES - 252,sp. */
this_strategy_size = SIZE_ADD_SP (size + 4 * num_regs_to_save
+ REG_SAVE_BYTES - 252);
/* Insn: fmov (##,sp),fs#, fo each fs# to be restored. */
this_strategy_size += SIZE_FMOV_SP (252 - REG_SAVE_BYTES
- 4 * num_regs_to_save,
num_regs_to_save);
/* We're going to use ret to release the FP registers
save area, so, no savings. */
 
if (this_strategy_size < strategy_size)
{
strategy = restore_sp_partial_adjust;
strategy_size = this_strategy_size;
}
}
 
/* Consider using a1 in post-increment mode, as long as the
user hasn't changed the calling conventions of a1. */
if (call_used_regs[FIRST_ADDRESS_REGNUM+1]
&& ! fixed_regs[FIRST_ADDRESS_REGNUM+1])
{
/* Insn: mov sp,a1. */
this_strategy_size = 1;
if (size)
{
/* Insn: add size,a1. */
this_strategy_size += SIZE_ADD_AX (size);
}
/* Insn: fmov (a1+),fs#, for each fs# to be restored. */
this_strategy_size += 3 * num_regs_to_save;
/* If size is large enough, we may be able to save a
couple of bytes. */
if (size + 4 * num_regs_to_save + REG_SAVE_BYTES > 255)
{
/* Insn: mov a1,sp. */
this_strategy_size += 2;
}
/* If we don't have to restore any non-FP registers,
we'll be able to save one byte by using rets. */
if (! REG_SAVE_BYTES)
this_strategy_size--;
 
if (this_strategy_size < strategy_size)
{
strategy = restore_a1;
strategy_size = this_strategy_size;
}
}
 
switch (strategy)
{
case restore_sp_post_adjust:
break;
 
case restore_sp_pre_adjust:
emit_insn (gen_addsi3 (stack_pointer_rtx,
stack_pointer_rtx,
GEN_INT (size)));
size = 0;
break;
 
case restore_sp_partial_adjust:
emit_insn (gen_addsi3 (stack_pointer_rtx,
stack_pointer_rtx,
GEN_INT (size + 4 * num_regs_to_save
+ REG_SAVE_BYTES - 252)));
size = 252 - REG_SAVE_BYTES - 4 * num_regs_to_save;
break;
case restore_a1:
reg = gen_rtx_REG (SImode, FIRST_ADDRESS_REGNUM + 1);
emit_insn (gen_movsi (reg, stack_pointer_rtx));
if (size)
emit_insn (gen_addsi3 (reg, reg, GEN_INT (size)));
break;
 
default:
gcc_unreachable ();
}
}
 
/* Adjust the selected register, if any, for post-increment. */
if (reg)
reg = gen_rtx_POST_INC (SImode, reg);
 
for (i = FIRST_FP_REGNUM; i <= LAST_FP_REGNUM; ++i)
if (regs_ever_live[i] && ! call_used_regs[i])
{
rtx addr;
if (reg)
addr = reg;
else if (size)
{
/* If we aren't using a post-increment register, use an
SP offset. */
addr = gen_rtx_PLUS (SImode,
stack_pointer_rtx,
GEN_INT (size));
}
else
addr = stack_pointer_rtx;
 
size += 4;
 
emit_insn (gen_movsi (gen_rtx_REG (SImode, i),
gen_rtx_MEM (SImode, addr)));
}
 
/* If we were using the restore_a1 strategy and the number of
bytes to be released won't fit in the `ret' byte, copy `a1'
to `sp', to avoid having to use `add' to adjust it. */
if (! frame_pointer_needed && reg && size + REG_SAVE_BYTES > 255)
{
emit_move_insn (stack_pointer_rtx, XEXP (reg, 0));
size = 0;
}
}
 
/* Maybe cut back the stack, except for the register save area.
 
If the frame pointer exists, then use the frame pointer to
cut back the stack.
 
If the stack size + register save area is more than 255 bytes,
then the stack must be cut back here since the size + register
save size is too big for a ret/retf instruction.
 
Else leave it alone, it will be cut back as part of the
ret/retf instruction, or there wasn't any stack to begin with.
 
Under no circumstances should the register save area be
deallocated here, that would leave a window where an interrupt
could occur and trash the register save area. */
if (frame_pointer_needed)
{
emit_move_insn (stack_pointer_rtx, frame_pointer_rtx);
size = 0;
}
else if (size + REG_SAVE_BYTES > 255)
{
emit_insn (gen_addsi3 (stack_pointer_rtx,
stack_pointer_rtx,
GEN_INT (size)));
size = 0;
}
 
/* Adjust the stack and restore callee-saved registers, if any. */
if (size || regs_ever_live[2] || regs_ever_live[3]
|| regs_ever_live[6] || regs_ever_live[7]
|| regs_ever_live[14] || regs_ever_live[15]
|| regs_ever_live[16] || regs_ever_live[17]
|| frame_pointer_needed)
emit_jump_insn (gen_return_internal_regs
(GEN_INT (size + REG_SAVE_BYTES)));
else
emit_jump_insn (gen_return_internal ());
}
 
/* Update the condition code from the insn. */
 
void
notice_update_cc (rtx body, rtx insn)
{
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;
break;
 
case CC_SET_ZN:
/* Insn sets the Z,N flags of CC to recog_data.operand[0].
V,C are unusable. */
CC_STATUS_INIT;
cc_status.flags |= CC_NO_CARRY | CC_OVERFLOW_UNUSABLE;
cc_status.value1 = recog_data.operand[0];
break;
 
case CC_SET_ZNV:
/* Insn sets the Z,N,V flags of CC to recog_data.operand[0].
C is unusable. */
CC_STATUS_INIT;
cc_status.flags |= CC_NO_CARRY;
cc_status.value1 = recog_data.operand[0];
break;
 
case CC_COMPARE:
/* The insn is a compare instruction. */
CC_STATUS_INIT;
cc_status.value1 = SET_SRC (body);
if (GET_CODE (cc_status.value1) == COMPARE
&& GET_MODE (XEXP (cc_status.value1, 0)) == SFmode)
cc_status.mdep.fpCC = 1;
break;
 
case CC_CLOBBER:
/* Insn doesn't leave CC in a usable state. */
CC_STATUS_INIT;
break;
 
default:
gcc_unreachable ();
}
}
 
/* Recognize the PARALLEL rtx generated by mn10300_gen_multiple_store().
This function is for MATCH_PARALLEL and so assumes OP is known to be
parallel. If OP is a multiple store, return a mask indicating which
registers it saves. Return 0 otherwise. */
 
int
store_multiple_operation (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
{
int count;
int mask;
int i;
unsigned int last;
rtx elt;
 
count = XVECLEN (op, 0);
if (count < 2)
return 0;
 
/* Check that first instruction has the form (set (sp) (plus A B)) */
elt = XVECEXP (op, 0, 0);
if (GET_CODE (elt) != SET
|| GET_CODE (SET_DEST (elt)) != REG
|| REGNO (SET_DEST (elt)) != STACK_POINTER_REGNUM
|| GET_CODE (SET_SRC (elt)) != PLUS)
return 0;
 
/* Check that A is the stack pointer and B is the expected stack size.
For OP to match, each subsequent instruction should push a word onto
the stack. We therefore expect the first instruction to create
COUNT-1 stack slots. */
elt = SET_SRC (elt);
if (GET_CODE (XEXP (elt, 0)) != REG
|| REGNO (XEXP (elt, 0)) != STACK_POINTER_REGNUM
|| GET_CODE (XEXP (elt, 1)) != CONST_INT
|| INTVAL (XEXP (elt, 1)) != -(count - 1) * 4)
return 0;
 
/* Now go through the rest of the vector elements. They must be
ordered so that the first instruction stores the highest-numbered
register to the highest stack slot and that subsequent instructions
store a lower-numbered register to the slot below.
 
LAST keeps track of the smallest-numbered register stored so far.
MASK is the set of stored registers. */
last = LAST_EXTENDED_REGNUM + 1;
mask = 0;
for (i = 1; i < count; i++)
{
/* Check that element i is a (set (mem M) R) and that R is valid. */
elt = XVECEXP (op, 0, i);
if (GET_CODE (elt) != SET
|| GET_CODE (SET_DEST (elt)) != MEM
|| GET_CODE (SET_SRC (elt)) != REG
|| REGNO (SET_SRC (elt)) >= last)
return 0;
 
/* R was OK, so provisionally add it to MASK. We return 0 in any
case if the rest of the instruction has a flaw. */
last = REGNO (SET_SRC (elt));
mask |= (1 << last);
 
/* Check that M has the form (plus (sp) (const_int -I*4)) */
elt = XEXP (SET_DEST (elt), 0);
if (GET_CODE (elt) != PLUS
|| GET_CODE (XEXP (elt, 0)) != REG
|| REGNO (XEXP (elt, 0)) != STACK_POINTER_REGNUM
|| GET_CODE (XEXP (elt, 1)) != CONST_INT
|| INTVAL (XEXP (elt, 1)) != -i * 4)
return 0;
}
 
/* All or none of the callee-saved extended registers must be in the set. */
if ((mask & 0x3c000) != 0
&& (mask & 0x3c000) != 0x3c000)
return 0;
 
return mask;
}
 
/* What (if any) secondary registers are needed to move IN with mode
MODE into a register in register class CLASS.
 
We might be able to simplify this. */
enum reg_class
mn10300_secondary_reload_class (enum reg_class class, enum machine_mode mode,
rtx in)
{
/* Memory loads less than a full word wide can't have an
address or stack pointer destination. They must use
a data register as an intermediate register. */
if ((GET_CODE (in) == MEM
|| (GET_CODE (in) == REG
&& REGNO (in) >= FIRST_PSEUDO_REGISTER)
|| (GET_CODE (in) == SUBREG
&& GET_CODE (SUBREG_REG (in)) == REG
&& REGNO (SUBREG_REG (in)) >= FIRST_PSEUDO_REGISTER))
&& (mode == QImode || mode == HImode)
&& (class == ADDRESS_REGS || class == SP_REGS
|| class == SP_OR_ADDRESS_REGS))
{
if (TARGET_AM33)
return DATA_OR_EXTENDED_REGS;
return DATA_REGS;
}
 
/* We can't directly load sp + const_int into a data register;
we must use an address register as an intermediate. */
if (class != SP_REGS
&& class != ADDRESS_REGS
&& class != SP_OR_ADDRESS_REGS
&& class != SP_OR_EXTENDED_REGS
&& class != ADDRESS_OR_EXTENDED_REGS
&& class != SP_OR_ADDRESS_OR_EXTENDED_REGS
&& (in == stack_pointer_rtx
|| (GET_CODE (in) == PLUS
&& (XEXP (in, 0) == stack_pointer_rtx
|| XEXP (in, 1) == stack_pointer_rtx))))
return ADDRESS_REGS;
 
if (GET_CODE (in) == PLUS
&& (XEXP (in, 0) == stack_pointer_rtx
|| XEXP (in, 1) == stack_pointer_rtx))
{
if (TARGET_AM33)
return DATA_OR_EXTENDED_REGS;
return DATA_REGS;
}
if (TARGET_AM33_2 && class == FP_REGS
&& GET_CODE (in) == MEM && ! OK_FOR_Q (in))
{
if (TARGET_AM33)
return DATA_OR_EXTENDED_REGS;
return DATA_REGS;
}
 
/* Otherwise assume no secondary reloads are needed. */
return NO_REGS;
}
 
int
initial_offset (int from, int to)
{
/* The difference between the argument pointer and the frame pointer
is the size of the callee register save area. */
if (from == ARG_POINTER_REGNUM && to == FRAME_POINTER_REGNUM)
{
if (regs_ever_live[2] || regs_ever_live[3]
|| regs_ever_live[6] || regs_ever_live[7]
|| regs_ever_live[14] || regs_ever_live[15]
|| regs_ever_live[16] || regs_ever_live[17]
|| fp_regs_to_save ()
|| frame_pointer_needed)
return REG_SAVE_BYTES
+ 4 * fp_regs_to_save ();
else
return 0;
}
 
/* The difference between the argument pointer and the stack pointer is
the sum of the size of this function's frame, the callee register save
area, and the fixed stack space needed for function calls (if any). */
if (from == ARG_POINTER_REGNUM && to == STACK_POINTER_REGNUM)
{
if (regs_ever_live[2] || regs_ever_live[3]
|| regs_ever_live[6] || regs_ever_live[7]
|| regs_ever_live[14] || regs_ever_live[15]
|| regs_ever_live[16] || regs_ever_live[17]
|| fp_regs_to_save ()
|| frame_pointer_needed)
return (get_frame_size () + REG_SAVE_BYTES
+ 4 * fp_regs_to_save ()
+ (current_function_outgoing_args_size
? current_function_outgoing_args_size + 4 : 0));
else
return (get_frame_size ()
+ (current_function_outgoing_args_size
? current_function_outgoing_args_size + 4 : 0));
}
 
/* The difference between the frame pointer and stack pointer is the sum
of the size of this function's frame and the fixed stack space needed
for function calls (if any). */
if (from == FRAME_POINTER_REGNUM && to == STACK_POINTER_REGNUM)
return (get_frame_size ()
+ (current_function_outgoing_args_size
? current_function_outgoing_args_size + 4 : 0));
 
gcc_unreachable ();
}
 
/* Worker function for TARGET_RETURN_IN_MEMORY. */
 
static bool
mn10300_return_in_memory (tree type, tree fntype ATTRIBUTE_UNUSED)
{
/* Return values > 8 bytes in length in memory. */
return (int_size_in_bytes (type) > 8
|| int_size_in_bytes (type) == 0
|| TYPE_MODE (type) == BLKmode);
}
 
/* Flush the argument registers to the stack for a stdarg function;
return the new argument pointer. */
static rtx
mn10300_builtin_saveregs (void)
{
rtx offset, mem;
tree fntype = TREE_TYPE (current_function_decl);
int argadj = ((!(TYPE_ARG_TYPES (fntype) != 0
&& (TREE_VALUE (tree_last (TYPE_ARG_TYPES (fntype)))
!= void_type_node)))
? UNITS_PER_WORD : 0);
int set = get_varargs_alias_set ();
 
if (argadj)
offset = plus_constant (current_function_arg_offset_rtx, argadj);
else
offset = current_function_arg_offset_rtx;
 
mem = gen_rtx_MEM (SImode, current_function_internal_arg_pointer);
set_mem_alias_set (mem, set);
emit_move_insn (mem, gen_rtx_REG (SImode, 0));
 
mem = gen_rtx_MEM (SImode,
plus_constant (current_function_internal_arg_pointer, 4));
set_mem_alias_set (mem, set);
emit_move_insn (mem, gen_rtx_REG (SImode, 1));
 
return copy_to_reg (expand_binop (Pmode, add_optab,
current_function_internal_arg_pointer,
offset, 0, 0, OPTAB_LIB_WIDEN));
}
 
void
mn10300_va_start (tree valist, rtx nextarg)
{
nextarg = expand_builtin_saveregs ();
std_expand_builtin_va_start (valist, nextarg);
}
 
/* Return true when a parameter should be passed by reference. */
 
static bool
mn10300_pass_by_reference (CUMULATIVE_ARGS *cum ATTRIBUTE_UNUSED,
enum machine_mode mode, tree type,
bool named ATTRIBUTE_UNUSED)
{
unsigned HOST_WIDE_INT size;
 
if (type)
size = int_size_in_bytes (type);
else
size = GET_MODE_SIZE (mode);
 
return (size > 8 || size == 0);
}
 
/* Return an RTX to represent where a value with mode MODE will be returned
from a function. If the result is 0, the argument is pushed. */
 
rtx
function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode,
tree type, int named ATTRIBUTE_UNUSED)
{
rtx result = 0;
int size, align;
 
/* We only support using 2 data registers as argument registers. */
int nregs = 2;
 
/* Figure out the size of the object to be passed. */
if (mode == BLKmode)
size = int_size_in_bytes (type);
else
size = GET_MODE_SIZE (mode);
 
/* Figure out the alignment of the object to be passed. */
align = size;
 
cum->nbytes = (cum->nbytes + 3) & ~3;
 
/* Don't pass this arg via a register if all the argument registers
are used up. */
if (cum->nbytes > nregs * UNITS_PER_WORD)
return 0;
 
/* Don't pass this arg via a register if it would be split between
registers and memory. */
if (type == NULL_TREE
&& cum->nbytes + size > nregs * UNITS_PER_WORD)
return 0;
 
switch (cum->nbytes / UNITS_PER_WORD)
{
case 0:
result = gen_rtx_REG (mode, 0);
break;
case 1:
result = gen_rtx_REG (mode, 1);
break;
default:
result = 0;
}
 
return result;
}
 
/* Return the number of bytes of registers to use for an argument passed
partially in registers and partially in memory. */
 
static int
mn10300_arg_partial_bytes (CUMULATIVE_ARGS *cum, enum machine_mode mode,
tree type, bool named ATTRIBUTE_UNUSED)
{
int size, align;
 
/* We only support using 2 data registers as argument registers. */
int nregs = 2;
 
/* Figure out the size of the object to be passed. */
if (mode == BLKmode)
size = int_size_in_bytes (type);
else
size = GET_MODE_SIZE (mode);
 
/* Figure out the alignment of the object to be passed. */
align = size;
 
cum->nbytes = (cum->nbytes + 3) & ~3;
 
/* Don't pass this arg via a register if all the argument registers
are used up. */
if (cum->nbytes > nregs * UNITS_PER_WORD)
return 0;
 
if (cum->nbytes + size <= nregs * UNITS_PER_WORD)
return 0;
 
/* Don't pass this arg via a register if it would be split between
registers and memory. */
if (type == NULL_TREE
&& cum->nbytes + size > nregs * UNITS_PER_WORD)
return 0;
 
return nregs * UNITS_PER_WORD - cum->nbytes;
}
 
/* Return the location of the function's value. This will be either
$d0 for integer functions, $a0 for pointers, or a PARALLEL of both
$d0 and $a0 if the -mreturn-pointer-on-do flag is set. Note that
we only return the PARALLEL for outgoing values; we do not want
callers relying on this extra copy. */
 
rtx
mn10300_function_value (tree valtype, tree func, int outgoing)
{
rtx rv;
enum machine_mode mode = TYPE_MODE (valtype);
 
if (! POINTER_TYPE_P (valtype))
return gen_rtx_REG (mode, FIRST_DATA_REGNUM);
else if (! TARGET_PTR_A0D0 || ! outgoing
|| current_function_returns_struct)
return gen_rtx_REG (mode, FIRST_ADDRESS_REGNUM);
 
rv = gen_rtx_PARALLEL (mode, rtvec_alloc (2));
XVECEXP (rv, 0, 0)
= gen_rtx_EXPR_LIST (VOIDmode,
gen_rtx_REG (mode, FIRST_ADDRESS_REGNUM),
GEN_INT (0));
XVECEXP (rv, 0, 1)
= gen_rtx_EXPR_LIST (VOIDmode,
gen_rtx_REG (mode, FIRST_DATA_REGNUM),
GEN_INT (0));
return rv;
}
 
/* Output a tst insn. */
const char *
output_tst (rtx operand, rtx insn)
{
rtx temp;
int past_call = 0;
 
/* We can save a byte if we can find a register which has the value
zero in it. */
temp = PREV_INSN (insn);
while (optimize && temp)
{
rtx set;
 
/* We allow the search to go through call insns. We record
the fact that we've past a CALL_INSN and reject matches which
use call clobbered registers. */
if (GET_CODE (temp) == CODE_LABEL
|| GET_CODE (temp) == JUMP_INSN
|| GET_CODE (temp) == BARRIER)
break;
 
if (GET_CODE (temp) == CALL_INSN)
past_call = 1;
 
if (GET_CODE (temp) == NOTE)
{
temp = PREV_INSN (temp);
continue;
}
 
/* It must be an insn, see if it is a simple set. */
set = single_set (temp);
if (!set)
{
temp = PREV_INSN (temp);
continue;
}
 
/* Are we setting a data register to zero (this does not win for
address registers)?
 
If it's a call clobbered register, have we past a call?
 
Make sure the register we find isn't the same as ourself;
the mn10300 can't encode that.
 
??? reg_set_between_p return nonzero anytime we pass a CALL_INSN
so the code to detect calls here isn't doing anything useful. */
if (REG_P (SET_DEST (set))
&& SET_SRC (set) == CONST0_RTX (GET_MODE (SET_DEST (set)))
&& !reg_set_between_p (SET_DEST (set), temp, insn)
&& (REGNO_REG_CLASS (REGNO (SET_DEST (set)))
== REGNO_REG_CLASS (REGNO (operand)))
&& REGNO_REG_CLASS (REGNO (SET_DEST (set))) != EXTENDED_REGS
&& REGNO (SET_DEST (set)) != REGNO (operand)
&& (!past_call
|| !call_used_regs[REGNO (SET_DEST (set))]))
{
rtx xoperands[2];
xoperands[0] = operand;
xoperands[1] = SET_DEST (set);
 
output_asm_insn ("cmp %1,%0", xoperands);
return "";
}
 
if (REGNO_REG_CLASS (REGNO (operand)) == EXTENDED_REGS
&& REG_P (SET_DEST (set))
&& SET_SRC (set) == CONST0_RTX (GET_MODE (SET_DEST (set)))
&& !reg_set_between_p (SET_DEST (set), temp, insn)
&& (REGNO_REG_CLASS (REGNO (SET_DEST (set)))
!= REGNO_REG_CLASS (REGNO (operand)))
&& REGNO_REG_CLASS (REGNO (SET_DEST (set))) == EXTENDED_REGS
&& REGNO (SET_DEST (set)) != REGNO (operand)
&& (!past_call
|| !call_used_regs[REGNO (SET_DEST (set))]))
{
rtx xoperands[2];
xoperands[0] = operand;
xoperands[1] = SET_DEST (set);
 
output_asm_insn ("cmp %1,%0", xoperands);
return "";
}
temp = PREV_INSN (temp);
}
return "cmp 0,%0";
}
 
int
impossible_plus_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
{
if (GET_CODE (op) != PLUS)
return 0;
 
if (XEXP (op, 0) == stack_pointer_rtx
|| XEXP (op, 1) == stack_pointer_rtx)
return 1;
 
return 0;
}
 
/* Similarly, but when using a zero_extract pattern for a btst where
the source operand might end up in memory. */
int
mask_ok_for_mem_btst (int len, int bit)
{
unsigned int mask = 0;
 
while (len > 0)
{
mask |= (1 << bit);
bit++;
len--;
}
 
/* MASK must bit into an 8bit value. */
return (((mask & 0xff) == mask)
|| ((mask & 0xff00) == mask)
|| ((mask & 0xff0000) == mask)
|| ((mask & 0xff000000) == mask));
}
 
/* Return 1 if X contains a symbolic expression. We know these
expressions will have one of a few well defined forms, so
we need only check those forms. */
int
symbolic_operand (register rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
{
switch (GET_CODE (op))
{
case SYMBOL_REF:
case LABEL_REF:
return 1;
case CONST:
op = XEXP (op, 0);
return ((GET_CODE (XEXP (op, 0)) == SYMBOL_REF
|| GET_CODE (XEXP (op, 0)) == LABEL_REF)
&& GET_CODE (XEXP (op, 1)) == CONST_INT);
default:
return 0;
}
}
 
/* Try machine dependent ways of modifying an illegitimate address
to be legitimate. If we find one, return the new valid address.
This macro is used in only one place: `memory_address' in explow.c.
 
OLDX is the address as it was before break_out_memory_refs was called.
In some cases it is useful to look at this to decide what needs to be done.
 
MODE and WIN are passed so that this macro can use
GO_IF_LEGITIMATE_ADDRESS.
 
Normally it is always safe for this macro to do nothing. It exists to
recognize opportunities to optimize the output.
 
But on a few ports with segmented architectures and indexed addressing
(mn10300, hppa) it is used to rewrite certain problematical addresses. */
rtx
legitimize_address (rtx x, rtx oldx ATTRIBUTE_UNUSED,
enum machine_mode mode ATTRIBUTE_UNUSED)
{
if (flag_pic && ! legitimate_pic_operand_p (x))
x = legitimize_pic_address (oldx, NULL_RTX);
 
/* Uh-oh. We might have an address for x[n-100000]. This needs
special handling to avoid creating an indexed memory address
with x-100000 as the base. */
if (GET_CODE (x) == PLUS
&& symbolic_operand (XEXP (x, 1), VOIDmode))
{
/* Ugly. We modify things here so that the address offset specified
by the index expression is computed first, then added to x to form
the entire address. */
 
rtx regx1, regy1, regy2, y;
 
/* Strip off any CONST. */
y = XEXP (x, 1);
if (GET_CODE (y) == CONST)
y = XEXP (y, 0);
 
if (GET_CODE (y) == PLUS || GET_CODE (y) == MINUS)
{
regx1 = force_reg (Pmode, force_operand (XEXP (x, 0), 0));
regy1 = force_reg (Pmode, force_operand (XEXP (y, 0), 0));
regy2 = force_reg (Pmode, force_operand (XEXP (y, 1), 0));
regx1 = force_reg (Pmode,
gen_rtx_fmt_ee (GET_CODE (y), Pmode, regx1, regy2));
return force_reg (Pmode, gen_rtx_PLUS (Pmode, regx1, regy1));
}
}
return x;
}
 
/* Convert a non-PIC address in `orig' to a PIC address using @GOT or
@GOTOFF in `reg'. */
rtx
legitimize_pic_address (rtx orig, rtx reg)
{
if (GET_CODE (orig) == LABEL_REF
|| (GET_CODE (orig) == SYMBOL_REF
&& (CONSTANT_POOL_ADDRESS_P (orig)
|| ! MN10300_GLOBAL_P (orig))))
{
if (reg == 0)
reg = gen_reg_rtx (Pmode);
 
emit_insn (gen_symGOTOFF2reg (reg, orig));
return reg;
}
else if (GET_CODE (orig) == SYMBOL_REF)
{
if (reg == 0)
reg = gen_reg_rtx (Pmode);
 
emit_insn (gen_symGOT2reg (reg, orig));
return reg;
}
return orig;
}
 
/* Return zero if X references a SYMBOL_REF or LABEL_REF whose symbol
isn't protected by a PIC unspec; nonzero otherwise. */
int
legitimate_pic_operand_p (rtx x)
{
register const char *fmt;
register int i;
 
if (GET_CODE (x) == SYMBOL_REF || GET_CODE (x) == LABEL_REF)
return 0;
 
if (GET_CODE (x) == UNSPEC
&& (XINT (x, 1) == UNSPEC_PIC
|| XINT (x, 1) == UNSPEC_GOT
|| XINT (x, 1) == UNSPEC_GOTOFF
|| XINT (x, 1) == UNSPEC_PLT))
return 1;
 
fmt = GET_RTX_FORMAT (GET_CODE (x));
for (i = GET_RTX_LENGTH (GET_CODE (x)) - 1; i >= 0; i--)
{
if (fmt[i] == 'E')
{
register int j;
 
for (j = XVECLEN (x, i) - 1; j >= 0; j--)
if (! legitimate_pic_operand_p (XVECEXP (x, i, j)))
return 0;
}
else if (fmt[i] == 'e' && ! legitimate_pic_operand_p (XEXP (x, i)))
return 0;
}
 
return 1;
}
 
/* Return TRUE if the address X, taken from a (MEM:MODE X) rtx, is
legitimate, and FALSE otherwise. */
bool
legitimate_address_p (enum machine_mode mode, rtx x, int strict)
{
if (CONSTANT_ADDRESS_P (x)
&& (! flag_pic || legitimate_pic_operand_p (x)))
return TRUE;
 
if (RTX_OK_FOR_BASE_P (x, strict))
return TRUE;
 
if (TARGET_AM33
&& GET_CODE (x) == POST_INC
&& RTX_OK_FOR_BASE_P (XEXP (x, 0), strict)
&& (mode == SImode || mode == SFmode || mode == HImode))
return TRUE;
 
if (GET_CODE (x) == PLUS)
{
rtx base = 0, index = 0;
 
if (REG_P (XEXP (x, 0))
&& REGNO_STRICT_OK_FOR_BASE_P (REGNO (XEXP (x, 0)), strict))
{
base = XEXP (x, 0);
index = XEXP (x, 1);
}
 
if (REG_P (XEXP (x, 1))
&& REGNO_STRICT_OK_FOR_BASE_P (REGNO (XEXP (x, 1)), strict))
{
base = XEXP (x, 1);
index = XEXP (x, 0);
}
 
if (base != 0 && index != 0)
{
if (GET_CODE (index) == CONST_INT)
return TRUE;
if (GET_CODE (index) == CONST
&& GET_CODE (XEXP (index, 0)) != PLUS
&& (! flag_pic
|| legitimate_pic_operand_p (index)))
return TRUE;
}
}
 
return FALSE;
}
 
static int
mn10300_address_cost_1 (rtx x, int *unsig)
{
switch (GET_CODE (x))
{
case REG:
switch (REGNO_REG_CLASS (REGNO (x)))
{
case SP_REGS:
*unsig = 1;
return 0;
 
case ADDRESS_REGS:
return 1;
 
case DATA_REGS:
case EXTENDED_REGS:
case FP_REGS:
return 3;
 
case NO_REGS:
return 5;
 
default:
gcc_unreachable ();
}
 
case PLUS:
case MINUS:
case ASHIFT:
case AND:
case IOR:
return (mn10300_address_cost_1 (XEXP (x, 0), unsig)
+ mn10300_address_cost_1 (XEXP (x, 1), unsig));
 
case EXPR_LIST:
case SUBREG:
case MEM:
return mn10300_address_cost (XEXP (x, 0));
 
case ZERO_EXTEND:
*unsig = 1;
return mn10300_address_cost_1 (XEXP (x, 0), unsig);
 
case CONST_INT:
if (INTVAL (x) == 0)
return 0;
if (INTVAL (x) + (*unsig ? 0 : 0x80) < 0x100)
return 1;
if (INTVAL (x) + (*unsig ? 0 : 0x8000) < 0x10000)
return 3;
if (INTVAL (x) + (*unsig ? 0 : 0x800000) < 0x1000000)
return 5;
return 7;
 
case CONST:
case SYMBOL_REF:
case LABEL_REF:
return 8;
 
default:
gcc_unreachable ();
 
}
}
 
static int
mn10300_address_cost (rtx x)
{
int s = 0;
return mn10300_address_cost_1 (x, &s);
}
 
static bool
mn10300_rtx_costs (rtx x, int code, int outer_code, int *total)
{
switch (code)
{
case CONST_INT:
/* Zeros are extremely cheap. */
if (INTVAL (x) == 0 && outer_code == SET)
*total = 0;
/* If it fits in 8 bits, then it's still relatively cheap. */
else if (INT_8_BITS (INTVAL (x)))
*total = 1;
/* This is the "base" cost, includes constants where either the
upper or lower 16bits are all zeros. */
else if (INT_16_BITS (INTVAL (x))
|| (INTVAL (x) & 0xffff) == 0
|| (INTVAL (x) & 0xffff0000) == 0)
*total = 2;
else
*total = 4;
return true;
 
case CONST:
case LABEL_REF:
case SYMBOL_REF:
/* These are more costly than a CONST_INT, but we can relax them,
so they're less costly than a CONST_DOUBLE. */
*total = 6;
return true;
 
case CONST_DOUBLE:
/* We don't optimize CONST_DOUBLEs well nor do we relax them well,
so their cost is very high. */
*total = 8;
return true;
 
/* ??? This probably needs more work. */
case MOD:
case DIV:
case MULT:
*total = 8;
return true;
 
default:
return false;
}
}
 
/* Check whether a constant used to initialize a DImode or DFmode can
use a clr instruction. The code here must be kept in sync with
movdf and movdi. */
 
bool
mn10300_wide_const_load_uses_clr (rtx operands[2])
{
long val[2];
 
if (GET_CODE (operands[0]) != REG
|| REGNO_REG_CLASS (REGNO (operands[0])) != DATA_REGS)
return false;
 
switch (GET_CODE (operands[1]))
{
case CONST_INT:
{
rtx low, high;
split_double (operands[1], &low, &high);
val[0] = INTVAL (low);
val[1] = INTVAL (high);
}
break;
case CONST_DOUBLE:
if (GET_MODE (operands[1]) == DFmode)
{
REAL_VALUE_TYPE rv;
 
REAL_VALUE_FROM_CONST_DOUBLE (rv, operands[1]);
REAL_VALUE_TO_TARGET_DOUBLE (rv, val);
}
else if (GET_MODE (operands[1]) == VOIDmode
|| GET_MODE (operands[1]) == DImode)
{
val[0] = CONST_DOUBLE_LOW (operands[1]);
val[1] = CONST_DOUBLE_HIGH (operands[1]);
}
break;
default:
return false;
}
 
return val[0] == 0 || val[1] == 0;
}
/* If using PIC, mark a SYMBOL_REF for a non-global symbol so that we
may access it using GOTOFF instead of GOT. */
 
static void
mn10300_encode_section_info (tree decl, rtx rtl, int first ATTRIBUTE_UNUSED)
{
rtx symbol;
 
if (GET_CODE (rtl) != MEM)
return;
symbol = XEXP (rtl, 0);
if (GET_CODE (symbol) != SYMBOL_REF)
return;
 
if (flag_pic)
SYMBOL_REF_FLAG (symbol) = (*targetm.binds_local_p) (decl);
}
/mn10300.opt
0,0 → 1,40
; Options for the Matsushita MN10300 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/>.
 
mam33
Target
Target the AM33 processor
 
mam33-2
Target
Target the AM33/2.0 processor
 
mmult-bug
Target Report Mask(MULT_BUG)
Work around hardware multiply bug
 
; Ignored by the compiler
mrelax
Target RejectNegative
Enable linker relaxations
 
mreturn-pointer-on-d0
Target Report Mask(PTR_A0D0)
Return pointers in both a0 and d0
/t-mn10300
0,0 → 1,17
# We want fine grained libraries, so use the new code to build the
# floating point emulation libraries.
FPBIT = fp-bit.c
DPBIT = dp-bit.c
 
dp-bit.c: $(srcdir)/config/fp-bit.c
cat $(srcdir)/config/fp-bit.c > dp-bit.c
 
fp-bit.c: $(srcdir)/config/fp-bit.c
echo '#define FLOAT' > fp-bit.c
cat $(srcdir)/config/fp-bit.c >> fp-bit.c
 
MULTILIB_OPTIONS = mam33/mam33-2
MULTILIB_DIRNAMES = am33 am33-2
 
LIBGCC = stmp-multilib
INSTALL_LIBGCC = install-multilib
/t-linux
0,0 → 1,11
# We want fine grained libraries, so use the new code to build the
# floating point emulation libraries.
FPBIT = fp-bit.c
DPBIT = dp-bit.c
 
dp-bit.c: $(srcdir)/config/fp-bit.c
cat $(srcdir)/config/fp-bit.c > dp-bit.c
 
fp-bit.c: $(srcdir)/config/fp-bit.c
echo '#define FLOAT' > fp-bit.c
cat $(srcdir)/config/fp-bit.c >> fp-bit.c
/predicates.md
0,0 → 1,49
;; Predicate definitions for Matsushita MN10300.
;; 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 the operand is the 1.0f constant.
 
(define_predicate "const_1f_operand"
(match_code "const_int,const_double")
{
return (op == CONST1_RTX (SFmode));
})
 
;; Return 1 if X is a CONST_INT that is only 8 bits wide. This is
;; used for the btst insn which may examine memory or a register (the
;; memory variant only allows an unsigned 8 bit integer).
 
(define_predicate "const_8bit_operand"
(match_code "const_int")
{
return (GET_CODE (op) == CONST_INT
&& INTVAL (op) >= 0
&& INTVAL (op) < 256);
})
 
;; Return true if OP is a valid call operand.
 
(define_predicate "call_address_operand"
(match_code "symbol_ref,reg,unspec")
{
if (flag_pic)
return (EXTRA_CONSTRAINT (op, 'S') || GET_CODE (op) == REG);
 
return (GET_CODE (op) == SYMBOL_REF || GET_CODE (op) == REG);
})
/mn10300.h
0,0 → 1,1065
/* Definitions of target machine for GNU compiler.
Matsushita MN10300 series
Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
2007 Free Software Foundation, Inc.
Contributed by Jeff Law (law@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/>. */
 
 
#undef ASM_SPEC
#undef LIB_SPEC
#undef ENDFILE_SPEC
#undef LINK_SPEC
#define LINK_SPEC "%{mrelax:--relax}"
#undef STARTFILE_SPEC
#define STARTFILE_SPEC "%{!mno-crt0:%{!shared:%{pg:gcrt0%O%s}%{!pg:%{p:mcrt0%O%s}%{!p:crt0%O%s}}}}"
 
/* Names to predefine in the preprocessor for this target machine. */
 
#define TARGET_CPU_CPP_BUILTINS() \
do \
{ \
builtin_define ("__mn10300__"); \
builtin_define ("__MN10300__"); \
builtin_assert ("cpu=mn10300"); \
builtin_assert ("machine=mn10300"); \
} \
while (0)
 
#define CPP_SPEC "%{mam33:-D__AM33__} %{mam33-2:-D__AM33__=2 -D__AM33_2__}"
 
extern GTY(()) int mn10300_unspec_int_label_counter;
 
enum processor_type {
PROCESSOR_MN10300,
PROCESSOR_AM33,
PROCESSOR_AM33_2
};
 
extern enum processor_type mn10300_processor;
 
#define TARGET_AM33 (mn10300_processor >= PROCESSOR_AM33)
#define TARGET_AM33_2 (mn10300_processor == PROCESSOR_AM33_2)
 
#ifndef PROCESSOR_DEFAULT
#define PROCESSOR_DEFAULT PROCESSOR_MN10300
#endif
 
#define OVERRIDE_OPTIONS mn10300_override_options ()
 
/* Print subsidiary information on the compiler version in use. */
 
#define TARGET_VERSION fprintf (stderr, " (MN10300)");
 
/* 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 Matsushita MN1003. */
#define BITS_BIG_ENDIAN 0
 
/* Define this if most significant byte of a word is the lowest numbered. */
/* This is not true on the Matsushita MN10300. */
#define BYTES_BIG_ENDIAN 0
 
/* Define this if most significant word of a multiword number is lowest
numbered.
This is not true on the Matsushita MN10300. */
#define WORDS_BIG_ENDIAN 0
 
/* Width of a word, in units (bytes). */
#define UNITS_PER_WORD 4
 
/* Allocation boundary (in *bits*) for storing arguments in argument list. */
#define PARM_BOUNDARY 32
 
/* The stack goes in 32 bit lumps. */
#define STACK_BOUNDARY 32
 
/* Allocation boundary (in *bits*) for the code of a function.
8 is the minimum boundary; it's unclear if bigger alignments
would improve performance. */
#define FUNCTION_BOUNDARY 8
 
/* No data type wants to be aligned rounder than this. */
#define BIGGEST_ALIGNMENT 32
 
/* Alignment of field after `int : 0' in a structure. */
#define EMPTY_FIELD_BOUNDARY 32
 
/* Define this if move instructions will actually fail to work
when given unaligned data. */
#define STRICT_ALIGNMENT 1
 
/* Define this as 1 if `char' should by default be signed; else as 0. */
#define DEFAULT_SIGNED_CHAR 0
/* 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. */
 
#define FIRST_PSEUDO_REGISTER 50
 
/* Specify machine-specific register numbers. */
#define FIRST_DATA_REGNUM 0
#define LAST_DATA_REGNUM 3
#define FIRST_ADDRESS_REGNUM 4
#define LAST_ADDRESS_REGNUM 8
#define FIRST_EXTENDED_REGNUM 10
#define LAST_EXTENDED_REGNUM 17
#define FIRST_FP_REGNUM 18
#define LAST_FP_REGNUM 49
 
/* Specify the registers used for certain standard purposes.
The values of these macros are register numbers. */
 
/* Register to use for pushing function arguments. */
#define STACK_POINTER_REGNUM (LAST_ADDRESS_REGNUM+1)
 
/* Base register for access to local variables of the function. */
#define FRAME_POINTER_REGNUM (LAST_ADDRESS_REGNUM-1)
 
/* Base register for access to arguments of the function. This
is a fake register and will be eliminated into either the frame
pointer or stack pointer. */
#define ARG_POINTER_REGNUM LAST_ADDRESS_REGNUM
 
/* Register in which static-chain is passed to a function. */
#define STATIC_CHAIN_REGNUM (FIRST_ADDRESS_REGNUM+1)
 
/* 1 for registers that have pervasive standard uses
and are not available for the register allocator. */
 
#define FIXED_REGISTERS \
{ 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0 \
, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 \
, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 \
}
 
/* 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. */
 
#define CALL_USED_REGISTERS \
{ 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0 \
, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 \
, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 \
}
 
#define REG_ALLOC_ORDER \
{ 0, 1, 4, 5, 2, 3, 6, 7, 10, 11, 12, 13, 14, 15, 16, 17, 8, 9 \
, 42, 43, 44, 45, 46, 47, 48, 49, 34, 35, 36, 37, 38, 39, 40, 41 \
, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33 \
}
 
#define CONDITIONAL_REGISTER_USAGE \
{ \
unsigned int i; \
\
if (!TARGET_AM33) \
{ \
for (i = FIRST_EXTENDED_REGNUM; \
i <= LAST_EXTENDED_REGNUM; i++) \
fixed_regs[i] = call_used_regs[i] = 1; \
} \
if (!TARGET_AM33_2) \
{ \
for (i = FIRST_FP_REGNUM; \
i <= LAST_FP_REGNUM; \
i++) \
fixed_regs[i] = call_used_regs[i] = 1; \
} \
if (flag_pic) \
fixed_regs[PIC_OFFSET_TABLE_REGNUM] = \
call_used_regs[PIC_OFFSET_TABLE_REGNUM] = 1;\
}
 
/* Return number of consecutive hard regs needed starting at reg REGNO
to hold something of mode MODE.
 
This is ordinarily the length in words of a value of mode MODE
but can be less for certain modes in special long registers. */
 
#define HARD_REGNO_NREGS(REGNO, MODE) \
((GET_MODE_SIZE (MODE) + UNITS_PER_WORD - 1) / UNITS_PER_WORD)
 
/* Value is 1 if hard register REGNO can hold a value of machine-mode
MODE. */
 
#define HARD_REGNO_MODE_OK(REGNO, MODE) \
((REGNO_REG_CLASS (REGNO) == DATA_REGS \
|| (TARGET_AM33 && REGNO_REG_CLASS (REGNO) == ADDRESS_REGS) \
|| REGNO_REG_CLASS (REGNO) == EXTENDED_REGS) \
? ((REGNO) & 1) == 0 || GET_MODE_SIZE (MODE) <= 4 \
: ((REGNO) & 1) == 0 || GET_MODE_SIZE (MODE) == 4)
 
/* 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) \
(TARGET_AM33 \
|| MODE1 == MODE2 \
|| (GET_MODE_SIZE (MODE1) <= 4 && GET_MODE_SIZE (MODE2) <= 4))
 
/* 4 data, and effectively 3 address registers is small as far as I'm
concerned. */
#define SMALL_REGISTER_CLASSES 1
/* 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, DATA_REGS, ADDRESS_REGS, SP_REGS,
DATA_OR_ADDRESS_REGS, SP_OR_ADDRESS_REGS,
EXTENDED_REGS, DATA_OR_EXTENDED_REGS, ADDRESS_OR_EXTENDED_REGS,
SP_OR_EXTENDED_REGS, SP_OR_ADDRESS_OR_EXTENDED_REGS,
FP_REGS, FP_ACC_REGS,
GENERAL_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", "DATA_REGS", "ADDRESS_REGS", \
"SP_REGS", "DATA_OR_ADDRESS_REGS", "SP_OR_ADDRESS_REGS", \
"EXTENDED_REGS", \
"DATA_OR_EXTENDED_REGS", "ADDRESS_OR_EXTENDED_REGS", \
"SP_OR_EXTENDED_REGS", "SP_OR_ADDRESS_OR_EXTENDED_REGS", \
"FP_REGS", "FP_ACC_REGS", \
"GENERAL_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, 0 }, /* No regs */ \
{ 0x0000f, 0 }, /* DATA_REGS */ \
{ 0x001f0, 0 }, /* ADDRESS_REGS */ \
{ 0x00200, 0 }, /* SP_REGS */ \
{ 0x001ff, 0 }, /* DATA_OR_ADDRESS_REGS */\
{ 0x003f0, 0 }, /* SP_OR_ADDRESS_REGS */\
{ 0x3fc00, 0 }, /* EXTENDED_REGS */ \
{ 0x3fc0f, 0 }, /* DATA_OR_EXTENDED_REGS */ \
{ 0x3fdf0, 0 }, /* ADDRESS_OR_EXTENDED_REGS */ \
{ 0x3fe00, 0 }, /* SP_OR_EXTENDED_REGS */ \
{ 0x3fff0, 0 }, /* SP_OR_ADDRESS_OR_EXTENDED_REGS */ \
{ 0xfffc0000, 0x3ffff }, /* FP_REGS */ \
{ 0x03fc0000, 0 }, /* FP_ACC_REGS */ \
{ 0x3fdff, 0 }, /* GENERAL_REGS */ \
{ 0xffffffff, 0x3ffff } /* 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) <= LAST_DATA_REGNUM ? DATA_REGS : \
(REGNO) <= LAST_ADDRESS_REGNUM ? ADDRESS_REGS : \
(REGNO) == STACK_POINTER_REGNUM ? SP_REGS : \
(REGNO) <= LAST_EXTENDED_REGNUM ? EXTENDED_REGS : \
(REGNO) <= LAST_FP_REGNUM ? FP_REGS : \
NO_REGS)
 
/* The class value for index registers, and the one for base regs. */
#define INDEX_REG_CLASS DATA_OR_EXTENDED_REGS
#define BASE_REG_CLASS SP_OR_ADDRESS_REGS
 
/* Get reg_class from a letter such as appears in the machine description. */
 
#define REG_CLASS_FROM_LETTER(C) \
((C) == 'd' ? DATA_REGS : \
(C) == 'a' ? ADDRESS_REGS : \
(C) == 'y' ? SP_REGS : \
! TARGET_AM33 ? NO_REGS : \
(C) == 'x' ? EXTENDED_REGS : \
! TARGET_AM33_2 ? NO_REGS : \
(C) == 'f' ? FP_REGS : \
(C) == 'A' ? FP_ACC_REGS : \
NO_REGS)
 
/* Macros to check register numbers against specific register classes. */
 
/* 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. */
 
/* 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. */
 
#ifndef REG_OK_STRICT
# define REG_STRICT 0
#else
# define REG_STRICT 1
#endif
 
# define REGNO_IN_RANGE_P(regno,min,max,strict) \
(IN_RANGE ((regno), (min), (max)) \
|| ((strict) \
? (reg_renumber \
&& reg_renumber[(regno)] >= (min) \
&& reg_renumber[(regno)] <= (max)) \
: (regno) >= FIRST_PSEUDO_REGISTER))
 
#define REGNO_DATA_P(regno, strict) \
(REGNO_IN_RANGE_P ((regno), FIRST_DATA_REGNUM, LAST_DATA_REGNUM, \
(strict)))
#define REGNO_ADDRESS_P(regno, strict) \
(REGNO_IN_RANGE_P ((regno), FIRST_ADDRESS_REGNUM, LAST_ADDRESS_REGNUM, \
(strict)))
#define REGNO_SP_P(regno, strict) \
(REGNO_IN_RANGE_P ((regno), STACK_POINTER_REGNUM, STACK_POINTER_REGNUM, \
(strict)))
#define REGNO_EXTENDED_P(regno, strict) \
(REGNO_IN_RANGE_P ((regno), FIRST_EXTENDED_REGNUM, LAST_EXTENDED_REGNUM, \
(strict)))
#define REGNO_AM33_P(regno, strict) \
(REGNO_DATA_P ((regno), (strict)) || REGNO_ADDRESS_P ((regno), (strict)) \
|| REGNO_EXTENDED_P ((regno), (strict)))
#define REGNO_FP_P(regno, strict) \
(REGNO_IN_RANGE_P ((regno), FIRST_FP_REGNUM, LAST_FP_REGNUM, (strict)))
 
#define REGNO_STRICT_OK_FOR_BASE_P(regno, strict) \
(REGNO_SP_P ((regno), (strict)) \
|| REGNO_ADDRESS_P ((regno), (strict)) \
|| REGNO_EXTENDED_P ((regno), (strict)))
#define REGNO_OK_FOR_BASE_P(regno) \
(REGNO_STRICT_OK_FOR_BASE_P ((regno), REG_STRICT))
#define REG_OK_FOR_BASE_P(X) \
(REGNO_OK_FOR_BASE_P (REGNO (X)))
 
#define REGNO_STRICT_OK_FOR_BIT_BASE_P(regno, strict) \
(REGNO_SP_P ((regno), (strict)) || REGNO_ADDRESS_P ((regno), (strict)))
#define REGNO_OK_FOR_BIT_BASE_P(regno) \
(REGNO_STRICT_OK_FOR_BIT_BASE_P ((regno), REG_STRICT))
#define REG_OK_FOR_BIT_BASE_P(X) \
(REGNO_OK_FOR_BIT_BASE_P (REGNO (X)))
 
#define REGNO_STRICT_OK_FOR_INDEX_P(regno, strict) \
(REGNO_DATA_P ((regno), (strict)) || REGNO_EXTENDED_P ((regno), (strict)))
#define REGNO_OK_FOR_INDEX_P(regno) \
(REGNO_STRICT_OK_FOR_INDEX_P ((regno), REG_STRICT))
#define REG_OK_FOR_INDEX_P(X) \
(REGNO_OK_FOR_INDEX_P (REGNO (X)))
 
/* 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) \
((X) == stack_pointer_rtx && (CLASS) != SP_REGS \
? ADDRESS_OR_EXTENDED_REGS \
: (GET_CODE (X) == MEM \
|| (GET_CODE (X) == REG \
&& REGNO (X) >= FIRST_PSEUDO_REGISTER) \
|| (GET_CODE (X) == SUBREG \
&& GET_CODE (SUBREG_REG (X)) == REG \
&& REGNO (SUBREG_REG (X)) >= FIRST_PSEUDO_REGISTER) \
? LIMIT_RELOAD_CLASS (GET_MODE (X), CLASS) \
: (CLASS)))
 
#define PREFERRED_OUTPUT_RELOAD_CLASS(X,CLASS) \
(X == stack_pointer_rtx && CLASS != SP_REGS \
? ADDRESS_OR_EXTENDED_REGS : CLASS)
 
#define LIMIT_RELOAD_CLASS(MODE, CLASS) \
(!TARGET_AM33 && (MODE == QImode || MODE == HImode) ? DATA_REGS : CLASS)
 
#define SECONDARY_RELOAD_CLASS(CLASS,MODE,IN) \
mn10300_secondary_reload_class(CLASS,MODE,IN)
 
/* Return the maximum number of consecutive registers
needed to represent mode MODE in a register of class CLASS. */
 
#define CLASS_MAX_NREGS(CLASS, MODE) \
((GET_MODE_SIZE (MODE) + UNITS_PER_WORD - 1) / UNITS_PER_WORD)
 
/* A class that contains registers which the compiler must always
access in a mode that is the same size as the mode in which it
loaded the register. */
#define CLASS_CANNOT_CHANGE_SIZE FP_REGS
 
/* 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 INT_8_BITS(VALUE) ((unsigned) (VALUE) + 0x80 < 0x100)
#define INT_16_BITS(VALUE) ((unsigned) (VALUE) + 0x8000 < 0x10000)
 
#define CONST_OK_FOR_I(VALUE) ((VALUE) == 0)
#define CONST_OK_FOR_J(VALUE) ((VALUE) == 1)
#define CONST_OK_FOR_K(VALUE) ((VALUE) == 2)
#define CONST_OK_FOR_L(VALUE) ((VALUE) == 4)
#define CONST_OK_FOR_M(VALUE) ((VALUE) == 3)
#define CONST_OK_FOR_N(VALUE) ((VALUE) == 255 || (VALUE) == 65535)
 
#define CONST_OK_FOR_LETTER_P(VALUE, C) \
((C) == 'I' ? CONST_OK_FOR_I (VALUE) : \
(C) == 'J' ? CONST_OK_FOR_J (VALUE) : \
(C) == 'K' ? CONST_OK_FOR_K (VALUE) : \
(C) == 'L' ? CONST_OK_FOR_L (VALUE) : \
(C) == 'M' ? CONST_OK_FOR_M (VALUE) : \
(C) == 'N' ? CONST_OK_FOR_N (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' ? (GET_MODE_CLASS (GET_MODE (VALUE)) == MODE_FLOAT \
&& (VALUE) == CONST0_RTX (GET_MODE (VALUE))) : 0)
 
/* 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
 
/* 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) 4
 
#define ELIMINABLE_REGS \
{{ ARG_POINTER_REGNUM, STACK_POINTER_REGNUM}, \
{ ARG_POINTER_REGNUM, FRAME_POINTER_REGNUM}, \
{ FRAME_POINTER_REGNUM, STACK_POINTER_REGNUM}}
 
#define CAN_ELIMINATE(FROM, TO) 1
 
#define INITIAL_ELIMINATION_OFFSET(FROM, TO, OFFSET) \
OFFSET = initial_offset (FROM, TO)
 
/* We can debug without frame pointers on the mn10300, so eliminate
them whenever possible. */
#define FRAME_POINTER_REQUIRED 0
#define CAN_DEBUG_WITHOUT_FP
 
/* 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. */
 
#define RETURN_POPS_ARGS(FUNDECL,FUNTYPE,SIZE) 0
 
/* We use d0/d1 for passing parameters, so allocate 8 bytes of space
for a register flushback area. */
#define REG_PARM_STACK_SPACE(DECL) 8
#define OUTGOING_REG_PARM_STACK_SPACE
#define ACCUMULATE_OUTGOING_ARGS 1
 
/* So we can allocate space for return pointers once for the function
instead of around every call. */
#define STACK_POINTER_OFFSET 4
 
/* 1 if N is a possible register number for function argument passing.
On the MN10300, no registers are used in this way. */
 
#define FUNCTION_ARG_REGNO_P(N) ((N) <= 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 MN10300, this is a single integer, which is a number of bytes
of arguments scanned so far. */
 
#define CUMULATIVE_ARGS struct cum_arg
struct cum_arg {int nbytes; };
 
/* 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 MN10300, the offset starts at 0. */
 
#define INIT_CUMULATIVE_ARGS(CUM, FNTYPE, LIBNAME, INDIRECT, N_NAMED_ARGS) \
((CUM).nbytes = 0)
 
/* 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) + 3) & ~3 \
: (int_size_in_bytes (TYPE) + 3) & ~3))
 
/* 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 MN10300 all args are pushed. */
 
#define FUNCTION_ARG(CUM, MODE, TYPE, NAMED) \
function_arg (&CUM, MODE, TYPE, NAMED)
 
/* 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. */
 
#define FUNCTION_VALUE(VALTYPE, FUNC) \
mn10300_function_value (VALTYPE, FUNC, 0)
#define FUNCTION_OUTGOING_VALUE(VALTYPE, FUNC) \
mn10300_function_value (VALTYPE, FUNC, 1)
 
/* Define how to find the value returned by a library function
assuming the value has mode MODE. */
 
#define LIBCALL_VALUE(MODE) gen_rtx_REG (MODE, FIRST_DATA_REGNUM)
 
/* 1 if N is a possible register number for a function value. */
 
#define FUNCTION_VALUE_REGNO_P(N) \
((N) == FIRST_DATA_REGNUM || (N) == FIRST_ADDRESS_REGNUM)
 
#define DEFAULT_PCC_STRUCT_RETURN 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 1
 
/* Output assembler code to FILE to increment profiler label # LABELNO
for profiling a function entry. */
 
#define FUNCTION_PROFILER(FILE, LABELNO) ;
 
#define TRAMPOLINE_TEMPLATE(FILE) \
do { \
fprintf (FILE, "\tadd -4,sp\n"); \
fprintf (FILE, "\t.long 0x0004fffa\n"); \
fprintf (FILE, "\tmov (0,sp),a0\n"); \
fprintf (FILE, "\tadd 4,sp\n"); \
fprintf (FILE, "\tmov (13,a0),a1\n"); \
fprintf (FILE, "\tmov (17,a0),a0\n"); \
fprintf (FILE, "\tjmp (a0)\n"); \
fprintf (FILE, "\t.long 0\n"); \
fprintf (FILE, "\t.long 0\n"); \
} while (0)
 
/* Length in units of the trampoline for entering a nested function. */
 
#define TRAMPOLINE_SIZE 0x1b
 
#define TRAMPOLINE_ALIGNMENT 32
 
/* Emit RTL insns to initialize the variable parts of 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) \
{ \
emit_move_insn (gen_rtx_MEM (SImode, plus_constant ((TRAMP), 0x14)), \
(CXT)); \
emit_move_insn (gen_rtx_MEM (SImode, plus_constant ((TRAMP), 0x18)), \
(FNADDR)); \
}
/* A C expression whose value is RTL representing the value of the return
address for the frame COUNT steps up from the current frame.
 
On the mn10300, the return address is not at a constant location
due to the frame layout. Luckily, it is at a constant offset from
the argument pointer, so we define RETURN_ADDR_RTX to return a
MEM using arg_pointer_rtx. Reload will replace arg_pointer_rtx
with a reference to the stack/frame pointer + an appropriate offset. */
 
#define RETURN_ADDR_RTX(COUNT, FRAME) \
((COUNT == 0) \
? gen_rtx_MEM (Pmode, arg_pointer_rtx) \
: (rtx) 0)
 
/* Implement `va_start' for varargs and stdarg. */
#define EXPAND_BUILTIN_VA_START(valist, nextarg) \
mn10300_va_start (valist, nextarg)
/* 1 if X is an rtx for a constant that is a valid address. */
 
#define CONSTANT_ADDRESS_P(X) CONSTANT_P (X)
 
/* Extra constraints. */
#define OK_FOR_Q(OP) \
(GET_CODE (OP) == MEM && ! CONSTANT_ADDRESS_P (XEXP (OP, 0)))
 
#define OK_FOR_R(OP) \
(GET_CODE (OP) == MEM \
&& GET_MODE (OP) == QImode \
&& (CONSTANT_ADDRESS_P (XEXP (OP, 0)) \
|| (GET_CODE (XEXP (OP, 0)) == REG \
&& REG_OK_FOR_BIT_BASE_P (XEXP (OP, 0)) \
&& XEXP (OP, 0) != stack_pointer_rtx) \
|| (GET_CODE (XEXP (OP, 0)) == PLUS \
&& GET_CODE (XEXP (XEXP (OP, 0), 0)) == REG \
&& REG_OK_FOR_BIT_BASE_P (XEXP (XEXP (OP, 0), 0)) \
&& XEXP (XEXP (OP, 0), 0) != stack_pointer_rtx \
&& GET_CODE (XEXP (XEXP (OP, 0), 1)) == CONST_INT \
&& INT_8_BITS (INTVAL (XEXP (XEXP (OP, 0), 1))))))
#define OK_FOR_T(OP) \
(GET_CODE (OP) == MEM \
&& GET_MODE (OP) == QImode \
&& (GET_CODE (XEXP (OP, 0)) == REG \
&& REG_OK_FOR_BIT_BASE_P (XEXP (OP, 0)) \
&& XEXP (OP, 0) != stack_pointer_rtx))
 
#define EXTRA_CONSTRAINT(OP, C) \
((C) == 'R' ? OK_FOR_R (OP) \
: (C) == 'Q' ? OK_FOR_Q (OP) \
: (C) == 'S' && flag_pic \
? GET_CODE (OP) == UNSPEC && (XINT (OP, 1) == UNSPEC_PLT \
|| XINT (OP, 1) == UNSPEC_PIC) \
: (C) == 'S' ? GET_CODE (OP) == SYMBOL_REF \
: (C) == 'T' ? OK_FOR_T (OP) \
: 0)
 
/* Maximum number of registers that can appear in a valid memory address. */
 
#define MAX_REGS_PER_ADDRESS 2
 
#define HAVE_POST_INCREMENT (TARGET_AM33)
 
/* GO_IF_LEGITIMATE_ADDRESS recognizes an RTL expression
that is a valid memory address for an instruction.
The MODE argument is the machine mode for the MEM expression
that wants to use this address.
 
The other macros defined here are used only in GO_IF_LEGITIMATE_ADDRESS,
except for CONSTANT_ADDRESS_P which is actually
machine-independent.
 
On the mn10300, the value in the address register must be
in the same memory space/segment as the effective address.
 
This is problematical for reload since it does not understand
that base+index != index+base in a memory reference.
 
Note it is still possible to use reg+reg addressing modes,
it's just much more difficult. For a discussion of a possible
workaround and solution, see the comments in pa.c before the
function record_unscaled_index_insn_codes. */
 
/* Accept either REG or SUBREG where a register is valid. */
#define RTX_OK_FOR_BASE_P(X, strict) \
((REG_P (X) && REGNO_STRICT_OK_FOR_BASE_P (REGNO (X), \
(strict))) \
|| (GET_CODE (X) == SUBREG && REG_P (SUBREG_REG (X)) \
&& REGNO_STRICT_OK_FOR_BASE_P (REGNO (SUBREG_REG (X)), \
(strict))))
 
#define GO_IF_LEGITIMATE_ADDRESS(MODE, X, ADDR) \
do \
{ \
if (legitimate_address_p ((MODE), (X), REG_STRICT)) \
goto ADDR; \
} \
while (0)
 
/* Try machine-dependent ways of modifying an illegitimate address
to be legitimate. If we find one, return the new, valid address.
This macro is used in only one place: `memory_address' in explow.c.
 
OLDX is the address as it was before break_out_memory_refs was called.
In some cases it is useful to look at this to decide what needs to be done.
 
MODE and WIN are passed so that this macro can use
GO_IF_LEGITIMATE_ADDRESS.
 
It is always safe for this macro to do nothing. It exists to recognize
opportunities to optimize the output. */
 
#define LEGITIMIZE_ADDRESS(X, OLDX, MODE, WIN) \
{ rtx orig_x = (X); \
(X) = legitimize_address (X, OLDX, MODE); \
if ((X) != orig_x && memory_address_p (MODE, X)) \
goto WIN; }
 
/* Go to LABEL if ADDR (a legitimate address expression)
has an effect that depends on the machine mode it is used for. */
 
#define GO_IF_MODE_DEPENDENT_ADDRESS(ADDR,LABEL) \
if (GET_CODE (ADDR) == POST_INC) \
goto LABEL
 
/* 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) 1
 
/* Zero if this needs fixing up to become PIC. */
 
#define LEGITIMATE_PIC_OPERAND_P(X) (legitimate_pic_operand_p (X))
 
/* Register to hold the addressing base for
position independent code access to data items. */
#define PIC_OFFSET_TABLE_REGNUM PIC_REG
 
/* The name of the pseudo-symbol representing the Global Offset Table. */
#define GOT_SYMBOL_NAME "*_GLOBAL_OFFSET_TABLE_"
 
#define SYMBOLIC_CONST_P(X) \
((GET_CODE (X) == SYMBOL_REF || GET_CODE (X) == LABEL_REF) \
&& ! LEGITIMATE_PIC_OPERAND_P (X))
 
/* Non-global SYMBOL_REFs have SYMBOL_REF_FLAG enabled. */
#define MN10300_GLOBAL_P(X) (! SYMBOL_REF_FLAG (X))
 
/* Recognize machine-specific patterns that may appear within
constants. Used for PIC-specific UNSPECs. */
#define OUTPUT_ADDR_CONST_EXTRA(STREAM, X, FAIL) \
do \
if (GET_CODE (X) == UNSPEC && XVECLEN ((X), 0) == 1) \
{ \
switch (XINT ((X), 1)) \
{ \
case UNSPEC_INT_LABEL: \
asm_fprintf ((STREAM), ".%LLIL%d", \
INTVAL (XVECEXP ((X), 0, 0))); \
break; \
case UNSPEC_PIC: \
/* GLOBAL_OFFSET_TABLE or local symbols, no suffix. */ \
output_addr_const ((STREAM), XVECEXP ((X), 0, 0)); \
break; \
case UNSPEC_GOT: \
output_addr_const ((STREAM), XVECEXP ((X), 0, 0)); \
fputs ("@GOT", (STREAM)); \
break; \
case UNSPEC_GOTOFF: \
output_addr_const ((STREAM), XVECEXP ((X), 0, 0)); \
fputs ("@GOTOFF", (STREAM)); \
break; \
case UNSPEC_PLT: \
output_addr_const ((STREAM), XVECEXP ((X), 0, 0)); \
fputs ("@PLT", (STREAM)); \
break; \
default: \
goto FAIL; \
} \
break; \
} \
else \
goto FAIL; \
while (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 VAX. */
 
/* 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 CC_OVERFLOW_UNUSABLE 0x200
#define CC_NO_CARRY CC_NO_OVERFLOW
#define NOTICE_UPDATE_CC(EXP, INSN) notice_update_cc(EXP, INSN)
 
#define REGISTER_MOVE_COST(MODE, CLASS1, CLASS2) \
((CLASS1 == CLASS2 && (CLASS1 == ADDRESS_REGS || CLASS1 == DATA_REGS)) ? 2 :\
((CLASS1 == ADDRESS_REGS || CLASS1 == DATA_REGS) && \
(CLASS2 == ADDRESS_REGS || CLASS2 == DATA_REGS)) ? 4 : \
(CLASS1 == SP_REGS && CLASS2 == ADDRESS_REGS) ? 2 : \
(CLASS1 == ADDRESS_REGS && CLASS2 == SP_REGS) ? 4 : \
! TARGET_AM33 ? 6 : \
(CLASS1 == SP_REGS || CLASS2 == SP_REGS) ? 6 : \
(CLASS1 == CLASS2 && CLASS1 == EXTENDED_REGS) ? 6 : \
(CLASS1 == FP_REGS || CLASS2 == FP_REGS) ? 6 : \
(CLASS1 == EXTENDED_REGS || CLASS2 == EXTENDED_REGS) ? 4 : \
4)
 
/* Nonzero if access to memory by bytes or half words is no faster
than accessing full words. */
#define SLOW_BYTE_ACCESS 1
 
/* Dispatch tables on the mn10300 are extremely expensive in terms of code
and readonly data size. So we crank up the case threshold value to
encourage a series of if/else comparisons to implement many small switch
statements. In theory, this value could be increased much more if we
were solely optimizing for space, but we keep it "reasonable" to avoid
serious code efficiency lossage. */
#define CASE_VALUES_THRESHOLD 6
 
#define NO_FUNCTION_CSE
 
/* According expr.c, a value of around 6 should minimize code size, and
for the MN10300 series, that's our primary concern. */
#define MOVE_RATIO 6
 
#define TEXT_SECTION_ASM_OP "\t.section .text"
#define DATA_SECTION_ASM_OP "\t.section .data"
#define BSS_SECTION_ASM_OP "\t.section .bss"
 
#define ASM_COMMENT_START "#"
 
/* 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"
 
/* 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_ALIGNED_BSS(FILE, DECL, NAME, SIZE, ALIGN) \
asm_output_aligned_bss ((FILE), (DECL), (NAME), (SIZE), (ALIGN))
 
/* Globalizing directive for a label. */
#define GLOBAL_ASM_OP "\t.global "
 
/* This is how to output a reference to a user-level label named NAME.
`assemble_name' uses this. */
 
#undef ASM_OUTPUT_LABELREF
#define ASM_OUTPUT_LABELREF(FILE, NAME) \
fprintf (FILE, "_%s", (*targetm.strip_name_encoding) (NAME))
 
#define ASM_PN_FORMAT "%s___%lu"
 
/* This is how we tell the assembler that two symbols have the same value. */
 
#define ASM_OUTPUT_DEF(FILE,NAME1,NAME2) \
do { assemble_name(FILE, NAME1); \
fputs(" = ", FILE); \
assemble_name(FILE, NAME2); \
fputc('\n', FILE); } while (0)
 
 
/* How to refer to registers in assembler output.
This sequence is indexed by compiler's hard-register-number (see above). */
 
#define REGISTER_NAMES \
{ "d0", "d1", "d2", "d3", "a0", "a1", "a2", "a3", "ap", "sp", \
"r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7" \
, "fs0", "fs1", "fs2", "fs3", "fs4", "fs5", "fs6", "fs7" \
, "fs8", "fs9", "fs10", "fs11", "fs12", "fs13", "fs14", "fs15" \
, "fs16", "fs17", "fs18", "fs19", "fs20", "fs21", "fs22", "fs23" \
, "fs24", "fs25", "fs26", "fs27", "fs28", "fs29", "fs30", "fs31" \
}
 
#define ADDITIONAL_REGISTER_NAMES \
{ {"r8", 4}, {"r9", 5}, {"r10", 6}, {"r11", 7}, \
{"r12", 0}, {"r13", 1}, {"r14", 2}, {"r15", 3}, \
{"e0", 10}, {"e1", 11}, {"e2", 12}, {"e3", 13}, \
{"e4", 14}, {"e5", 15}, {"e6", 16}, {"e7", 17} \
, {"fd0", 18}, {"fd2", 20}, {"fd4", 22}, {"fd6", 24} \
, {"fd8", 26}, {"fd10", 28}, {"fd12", 30}, {"fd14", 32} \
, {"fd16", 34}, {"fd18", 36}, {"fd20", 38}, {"fd22", 40} \
, {"fd24", 42}, {"fd26", 44}, {"fd28", 46}, {"fd30", 48} \
}
 
/* Print an instruction operand X on file FILE.
look in mn10300.c for details */
 
#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 output-vax.c. */
 
#define PRINT_OPERAND_ADDRESS(FILE, ADDR) print_operand_address (FILE, ADDR)
 
#define ASM_OUTPUT_REG_PUSH(FILE,REGNO)
#define ASM_OUTPUT_REG_POP(FILE,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, "\t%s .L%d\n", ".long", 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, "\t%s .L%d-.L%d\n", ".long", VALUE, REL)
 
#define ASM_OUTPUT_ALIGN(FILE,LOG) \
if ((LOG) != 0) \
fprintf (FILE, "\t.align %d\n", (LOG))
 
/* We don't have to worry about dbx compatibility for the mn10300. */
#define DEFAULT_GDB_EXTENSIONS 1
 
/* Use dwarf2 debugging info by default. */
#undef PREFERRED_DEBUGGING_TYPE
#define PREFERRED_DEBUGGING_TYPE DWARF2_DEBUG
 
#define DWARF2_ASM_LINE_DEBUG_INFO 1
 
/* GDB always assumes the current function's frame begins at the value
of the stack pointer upon entry to the current function. Accessing
local variables and parameters passed on the stack is done using the
base of the frame + an offset provided by GCC.
 
For functions which have frame pointers this method works fine;
the (frame pointer) == (stack pointer at function entry) and GCC provides
an offset relative to the frame pointer.
 
This loses for functions without a frame pointer; GCC provides an offset
which is relative to the stack pointer after adjusting for the function's
frame size. GDB would prefer the offset to be relative to the value of
the stack pointer at the function's entry. Yuk! */
#define DEBUGGER_AUTO_OFFSET(X) \
((GET_CODE (X) == PLUS ? INTVAL (XEXP (X, 1)) : 0) \
+ (frame_pointer_needed \
? 0 : -initial_offset (FRAME_POINTER_REGNUM, STACK_POINTER_REGNUM)))
 
#define DEBUGGER_ARG_OFFSET(OFFSET, X) \
((GET_CODE (X) == PLUS ? OFFSET : 0) \
+ (frame_pointer_needed \
? 0 : -initial_offset (ARG_POINTER_REGNUM, STACK_POINTER_REGNUM)))
 
/* Specify the machine mode that this machine uses
for the index in the tablejump instruction. */
#define CASE_VECTOR_MODE Pmode
 
/* Define if operations between registers always perform the operation
on the full register even if a narrower mode is specified. */
#define WORD_REGISTER_OPERATIONS
 
#define LOAD_EXTEND_OP(MODE) ZERO_EXTEND
 
/* 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 4
 
/* 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 1
 
/* 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 SImode
 
/* 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
 
/* The assembler op to get a word. */
 
#define FILE_ASM_OP "\t.file\n"
 
typedef struct mn10300_cc_status_mdep
{
int fpCC;
}
cc_status_mdep;
 
#define CC_STATUS_MDEP cc_status_mdep
 
#define CC_STATUS_MDEP_INIT (cc_status.mdep.fpCC = 0)
/linux.h
0,0 → 1,86
/* Definitions of taret machine for GNU compiler.
Matsushita AM33/2.0
Copyright 2001, 2002, 2005, 2006, 2007 Free Software Foundation, Inc.
Contributed by Alexandre Oliva <aoliva@redhat.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/>. */
#undef PREFERRED_DEBUGGING_TYPE
#define PREFERRED_DEBUGGING_TYPE DWARF2_DEBUG
 
#define TARGET_OS_CPP_BUILTINS() LINUX_TARGET_OS_CPP_BUILTINS()
 
#undef CPP_SPEC
#define CPP_SPEC "%{mam33:-D__AM33__} %{!mam33:-D__AM33__=2 -D__AM33_2__} \
%{posix:-D_POSIX_SOURCE} \
%{pthread:-D_REENTRANT -D_PTHREADS}"
 
#undef ASM_SPEC
#define ASM_SPEC "%{Wa,*:%*}"
 
#define GLIBC_DYNAMIC_LINKER "/lib/ld.so.1"
 
#undef LINK_SPEC
#define LINK_SPEC "%{mrelax:--relax} %{shared:-shared} \
%{!static: \
%{rdynamic:-export-dynamic} \
%{!dynamic-linker:-dynamic-linker " LINUX_DYNAMIC_LINKER "}} \
%{static:-static}"
 
#undef PROCESSOR_DEFAULT
#define PROCESSOR_DEFAULT PROCESSOR_AM33_2
 
#undef TARGET_VERSION
#define TARGET_VERSION fprintf (stderr, " (AM33/2.0 GNU/Linux)");
 
#define DBX_REGISTER_NUMBER(REGNO) (REGNO)
 
extern int mn10300_protect_label;
 
#undef PRINT_OPERAND
#define PRINT_OPERAND(FILE, X, CODE) \
do \
{ \
mn10300_protect_label = 1; \
print_operand ((FILE), (X), (CODE)); \
mn10300_protect_label = 0; \
} \
while (0)
 
#undef PRINT_OPERAND_ADDRESS
#define PRINT_OPERAND_ADDRESS(FILE, X) \
do \
{ \
mn10300_protect_label = 1; \
print_operand_address ((FILE), (X)); \
mn10300_protect_label = 0; \
} \
while (0)
 
#undef ASM_OUTPUT_LABELREF
#define ASM_OUTPUT_LABELREF(FILE, NAME) \
do \
{ \
const char * real_name; \
\
real_name = (*targetm.strip_name_encoding) (NAME); \
if (mn10300_protect_label) \
asm_fprintf (FILE, "+"); \
asm_fprintf (FILE, "%U%s", real_name); \
} \
while (0)
 
/mn10300-protos.h
0,0 → 1,58
/* Definitions of target machine for GNU compiler. Matsushita MN10300 series
Copyright (C) 2000, 2003, 2004, 2005, 2007 Free Software Foundation, Inc.
Contributed by Jeff Law (law@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/>. */
 
#ifdef RTX_CODE
 
#ifdef TREE_CODE
extern void mn10300_va_start (tree, rtx);
#endif /* TREE_CODE */
 
extern void mn10300_override_options (void);
extern struct rtx_def *legitimize_address (rtx, rtx, enum machine_mode);
extern rtx legitimize_pic_address (rtx, rtx);
extern int legitimate_pic_operand_p (rtx);
extern bool legitimate_address_p (enum machine_mode, rtx, int);
extern void print_operand (FILE *, rtx, int);
extern void print_operand_address (FILE *, rtx);
extern void mn10300_print_reg_list (FILE *, int);
extern int mn10300_get_live_callee_saved_regs (void);
extern void mn10300_gen_multiple_store (int);
extern void notice_update_cc (rtx, rtx);
extern enum reg_class mn10300_secondary_reload_class (enum reg_class,
enum machine_mode, rtx);
extern const char *output_tst (rtx, rtx);
extern int store_multiple_operation (rtx, enum machine_mode);
extern int symbolic_operand (rtx, enum machine_mode);
extern int impossible_plus_operand (rtx, enum machine_mode);
 
extern bool mn10300_wide_const_load_uses_clr (rtx operands[2]);
#endif /* RTX_CODE */
 
#ifdef TREE_CODE
extern struct rtx_def *function_arg (CUMULATIVE_ARGS *,
enum machine_mode, tree, int);
extern rtx mn10300_function_value (tree, tree, int);
#endif /* TREE_CODE */
 
extern void expand_prologue (void);
extern void expand_epilogue (void);
extern int initial_offset (int, int);
extern int can_use_return_insn (void);
extern int mask_ok_for_mem_btst (int, int);
/mn10300.md
0,0 → 1,2673
;; GCC machine description for Matsushita MN10300
;; Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
;; 2007 Free Software Foundation, Inc.
;; Contributed by Jeff Law (law@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/>.
 
;; The original PO technology requires these to be ordered by speed,
;; so that assigner will pick the fastest.
 
;; See file "rtl.def" for documentation on define_insn, match_*, et. al.
 
;; 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; c is unusable.
;; set_zn - insn sets z,n to usable values; v,c are unusable.
;; compare - compare instruction
;; clobber - value of cc is unknown
(define_attr "cc" "none,none_0hit,set_znv,set_zn,compare,clobber"
(const_string "clobber"))
 
(define_constants [
(PIC_REG 6)
(SP_REG 9)
 
(UNSPEC_INT_LABEL 0)
(UNSPEC_PIC 1)
(UNSPEC_GOT 2)
(UNSPEC_GOTOFF 3)
(UNSPEC_PLT 4)
])
 
(include "predicates.md")
;; ----------------------------------------------------------------------
;; MOVE INSTRUCTIONS
;; ----------------------------------------------------------------------
 
;; movqi
 
(define_expand "movqi"
[(set (match_operand:QI 0 "general_operand" "")
(match_operand:QI 1 "general_operand" ""))]
""
"
{
/* One of the ops has to be in a register */
if (!register_operand (operand0, QImode)
&& !register_operand (operand1, QImode))
operands[1] = copy_to_mode_reg (QImode, operand1);
}")
 
(define_insn ""
[(set (match_operand:QI 0 "nonimmediate_operand" "=d*x*a*f,d*x,d*x*a,d*x*a,m,*f,d*x*a")
(match_operand:QI 1 "general_operand" "0,I,d*xai,m,d*xa,d*xa*f,*f"))]
"TARGET_AM33
&& (register_operand (operands[0], QImode)
|| register_operand (operands[1], QImode))"
"*
{
switch (which_alternative)
{
case 0:
return \"nop\";
case 1:
return \"clr %0\";
case 2:
if (GET_CODE (operands[1]) == CONST_DOUBLE)
{
rtx xoperands[2];
xoperands[0] = operands[0];
xoperands[1] = GEN_INT (CONST_DOUBLE_LOW (operands[1]));
output_asm_insn (\"mov %1,%0\", xoperands);
return \"\";
}
 
if (REGNO_REG_CLASS (true_regnum (operands[0])) == EXTENDED_REGS
&& GET_CODE (operands[1]) == CONST_INT)
{
HOST_WIDE_INT val = INTVAL (operands[1]);
 
if (((val & 0x80) && ! (val & 0xffffff00))
|| ((val & 0x800000) && ! (val & 0xff000000)))
return \"movu %1,%0\";
}
return \"mov %1,%0\";
case 3:
case 4:
return \"movbu %1,%0\";
case 5:
case 6:
return \"fmov %1,%0\";
default:
gcc_unreachable ();
}
}"
[(set_attr "cc" "none,clobber,none_0hit,none_0hit,none_0hit,none_0hit,none_0hit")])
 
(define_insn ""
[(set (match_operand:QI 0 "nonimmediate_operand" "=d*a,d,d*a,d,m")
(match_operand:QI 1 "general_operand" "0,I,dai,m,d"))]
"register_operand (operands[0], QImode)
|| register_operand (operands[1], QImode)"
"*
{
switch (which_alternative)
{
case 0:
return \"nop\";
case 1:
return \"clr %0\";
case 2:
if (GET_CODE (operands[1]) == CONST_DOUBLE)
{
rtx xoperands[2];
xoperands[0] = operands[0];
xoperands[1] = GEN_INT (CONST_DOUBLE_LOW (operands[1]));
output_asm_insn (\"mov %1,%0\", xoperands);
return \"\";
}
 
return \"mov %1,%0\";
case 3:
case 4:
return \"movbu %1,%0\";
default:
gcc_unreachable ();
}
}"
[(set_attr "cc" "none,clobber,none_0hit,none_0hit,none_0hit")])
 
;; movhi
 
(define_expand "movhi"
[(set (match_operand:HI 0 "general_operand" "")
(match_operand:HI 1 "general_operand" ""))]
""
"
{
/* 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 ""
[(set (match_operand:HI 0 "nonimmediate_operand" "=d*x*a*f,d*x,d*x*a,d*x*a,m,*f,d*x*a")
(match_operand:HI 1 "general_operand" "0,I,d*x*ai,m,d*x*a,d*x*a*f,*f"))]
"TARGET_AM33
&& (register_operand (operands[0], HImode)
|| register_operand (operands[1], HImode))"
"*
{
switch (which_alternative)
{
case 0:
return \"nop\";
case 1:
return \"clr %0\";
case 2:
if (GET_CODE (operands[1]) == CONST_DOUBLE)
{
rtx xoperands[2];
xoperands[0] = operands[0];
xoperands[1] = GEN_INT (CONST_DOUBLE_LOW (operands[1]));
output_asm_insn (\"mov %1,%0\", xoperands);
return \"\";
}
 
if (REGNO_REG_CLASS (true_regnum (operands[0])) == EXTENDED_REGS
&& GET_CODE (operands[1]) == CONST_INT)
{
HOST_WIDE_INT val = INTVAL (operands[1]);
 
if (((val & 0x80) && ! (val & 0xffffff00))
|| ((val & 0x800000) && ! (val & 0xff000000)))
return \"movu %1,%0\";
}
return \"mov %1,%0\";
case 3:
case 4:
return \"movhu %1,%0\";
case 5:
case 6:
return \"fmov %1,%0\";
default:
gcc_unreachable ();
}
}"
[(set_attr "cc" "none,clobber,none_0hit,none_0hit,none_0hit,none_0hit,none_0hit")])
 
(define_insn ""
[(set (match_operand:HI 0 "nonimmediate_operand" "=d*a,d,d*a,d,m")
(match_operand:HI 1 "general_operand" "0,I,dai,m,d"))]
"register_operand (operands[0], HImode)
|| register_operand (operands[1], HImode)"
"*
{
switch (which_alternative)
{
case 0:
return \"nop\";
case 1:
return \"clr %0\";
case 2:
if (GET_CODE (operands[1]) == CONST_DOUBLE)
{
rtx xoperands[2];
xoperands[0] = operands[0];
xoperands[1] = GEN_INT (CONST_DOUBLE_LOW (operands[1]));
output_asm_insn (\"mov %1,%0\", xoperands);
return \"\";
}
return \"mov %1,%0\";
case 3:
case 4:
return \"movhu %1,%0\";
default:
gcc_unreachable ();
}
}"
[(set_attr "cc" "none,clobber,none_0hit,none_0hit,none_0hit")])
 
;; movsi and helpers
 
;; We use this to handle addition of two values when one operand is the
;; stack pointer and the other is a memory reference of some kind. Reload
;; does not handle them correctly without this expander.
(define_expand "reload_insi"
[(set (match_operand:SI 0 "register_operand" "=a")
(match_operand:SI 1 "impossible_plus_operand" ""))
(clobber (match_operand:SI 2 "register_operand" "=&r"))]
""
"
{
if (XEXP (operands[1], 0) == stack_pointer_rtx)
{
if (GET_CODE (XEXP (operands[1], 1)) == SUBREG
&& (GET_MODE_SIZE (GET_MODE (XEXP (operands[1], 1)))
> GET_MODE_SIZE (GET_MODE (SUBREG_REG (XEXP (operands[1], 1))))))
emit_move_insn (operands[2],
gen_rtx_ZERO_EXTEND
(GET_MODE (XEXP (operands[1], 1)),
SUBREG_REG (XEXP (operands[1], 1))));
else
emit_move_insn (operands[2], XEXP (operands[1], 1));
emit_move_insn (operands[0], XEXP (operands[1], 0));
}
else
{
if (GET_CODE (XEXP (operands[1], 0)) == SUBREG
&& (GET_MODE_SIZE (GET_MODE (XEXP (operands[1], 0)))
> GET_MODE_SIZE (GET_MODE (SUBREG_REG (XEXP (operands[1], 0))))))
emit_move_insn (operands[2],
gen_rtx_ZERO_EXTEND
(GET_MODE (XEXP (operands[1], 0)),
SUBREG_REG (XEXP (operands[1], 0))));
else
emit_move_insn (operands[2], XEXP (operands[1], 0));
emit_move_insn (operands[0], XEXP (operands[1], 1));
}
emit_insn (gen_addsi3 (operands[0], operands[0], operands[2]));
DONE;
}")
 
(define_insn "pop_pic_reg"
[(set (reg:SI PIC_REG)
(mem:SI (post_inc:SI (reg:SI SP_REG))))]
"reload_completed"
"movm (sp),[a2]")
 
(define_expand "movsi"
[(set (match_operand:SI 0 "general_operand" "")
(match_operand:SI 1 "general_operand" ""))]
""
"
{
/* 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);
if (flag_pic)
{
rtx temp;
if (SYMBOLIC_CONST_P (operands[1]))
{
if (GET_CODE (operands[0]) == MEM)
operands[1] = force_reg (Pmode, operands[1]);
else
{
temp = no_new_pseudos ? operands[0] : gen_reg_rtx (Pmode);
operands[1] = legitimize_pic_address (operands[1], temp);
}
}
else if (GET_CODE (operands[1]) == CONST
&& GET_CODE (XEXP (operands[1], 0)) == PLUS
&& SYMBOLIC_CONST_P (XEXP (XEXP (operands[1], 0), 0)))
{
temp = no_new_pseudos ? operands[0] : gen_reg_rtx (Pmode);
temp = legitimize_pic_address (XEXP (XEXP (operands[1], 0), 0),
temp);
operands[1] = expand_binop (SImode, add_optab, temp,
XEXP (XEXP (operands[1], 0), 1),
no_new_pseudos ? temp
: gen_reg_rtx (Pmode),
0, OPTAB_LIB_WIDEN);
}
}
}")
 
(define_insn ""
[(set (match_operand:SI 0 "nonimmediate_operand"
"=dx,ax,dx,a,dxm,dxm,axm,axm,dx,dx,ax,ax,axR,!*y,*f,*f,dxaQ")
(match_operand:SI 1 "general_operand"
"0,0,I,I,dx,ax,dx,ax,dixm,aixm,dixm,aixm,!*y,axR,0,dxaQi*f,*f"))]
"register_operand (operands[0], SImode)
|| register_operand (operands[1], SImode)"
"*
{
switch (which_alternative)
{
case 0:
case 1:
return \"nop\";
case 2:
return \"clr %0\";
case 3:
case 4:
case 5:
case 6:
case 7:
case 8:
case 9:
case 10:
case 11:
case 12:
case 13:
if (GET_CODE (operands[1]) == CONST_DOUBLE)
{
rtx xoperands[2];
xoperands[0] = operands[0];
xoperands[1] = GEN_INT (CONST_DOUBLE_LOW (operands[1]));
output_asm_insn (\"mov %1,%0\", xoperands);
return \"\";
}
 
if (REGNO_REG_CLASS (true_regnum (operands[0])) == EXTENDED_REGS
&& GET_CODE (operands[1]) == CONST_INT)
{
HOST_WIDE_INT val = INTVAL (operands[1]);
 
if (((val & 0x80) && ! (val & 0xffffff00))
|| ((val & 0x800000) && ! (val & 0xff000000)))
return \"movu %1,%0\";
}
return \"mov %1,%0\";
case 14:
return \"nop\";
case 15:
case 16:
return \"fmov %1,%0\";
default:
gcc_unreachable ();
}
}"
[(set_attr "cc" "none,none,clobber,none_0hit,none_0hit,none_0hit,none_0hit,none_0hit,none_0hit,none_0hit,none_0hit,none_0hit,none_0hit,none_0hit,none,none_0hit,none_0hit")])
 
(define_expand "movsf"
[(set (match_operand:SF 0 "general_operand" "")
(match_operand:SF 1 "general_operand" ""))]
""
"
{
/* 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 ""
[(set (match_operand:SF 0 "nonimmediate_operand" "=f,dx,ax,dx,a,f,dxaQ,daxm,dax")
(match_operand:SF 1 "general_operand" "0,0,0,G,G,fdxaQF,f,dax,daxFm"))]
"register_operand (operands[0], SFmode)
|| register_operand (operands[1], SFmode)"
"*
{
switch (which_alternative)
{
case 0:
case 1:
case 2:
return \"nop\";
case 3:
return \"clr %0\";
/* case 4: below */
case 5:
case 6:
return \"fmov %1, %0\";
case 4:
case 7:
case 8:
if (REGNO_REG_CLASS (true_regnum (operands[0])) == EXTENDED_REGS
&& GET_CODE (operands[1]) == CONST_INT)
{
HOST_WIDE_INT val = INTVAL (operands[1]);
 
if (((val & 0x80) && ! (val & 0xffffff00))
|| ((val & 0x800000) && ! (val & 0xff000000)))
return \"movu %1,%0\";
}
return \"mov %1,%0\";
default:
gcc_unreachable ();
}
}"
[(set_attr "cc" "none,none,none,clobber,none_0hit,none_0hit,none_0hit,none_0hit,none_0hit")])
 
(define_expand "movdi"
[(set (match_operand:DI 0 "general_operand" "")
(match_operand:DI 1 "general_operand" ""))]
""
"
{
/* One of the ops has to be in a register */
if (!register_operand (operand1, DImode)
&& !register_operand (operand0, DImode))
operands[1] = copy_to_mode_reg (DImode, operand1);
}")
 
(define_insn ""
[(set (match_operand:DI 0 "nonimmediate_operand"
"=dx,ax,dx,a,dxm,dxm,axm,axm,dx,dx,ax,ax,*f,*f,*f,dxa,*f,Q")
(match_operand:DI 1 "general_operand"
"0,0,I,I,dx,ax,dx,ax,dxim,axim,dxim,axim,0,*f,dxai,*f,Q,*f"))]
"register_operand (operands[0], DImode)
|| register_operand (operands[1], DImode)"
"*
{
long val[2];
REAL_VALUE_TYPE rv;
 
switch (which_alternative)
{
case 0:
case 1:
return \"nop\";
 
case 2:
return \"clr %L0\;clr %H0\";
 
case 3:
if (rtx_equal_p (operands[0], operands[1]))
return \"sub %L1,%L0\;mov %L0,%H0\";
else
return \"mov %1,%L0\;mov %L0,%H0\";
case 4:
case 5:
case 6:
case 7:
case 8:
case 9:
case 10:
case 11:
if (GET_CODE (operands[1]) == CONST_INT)
{
rtx low, high;
split_double (operands[1], &low, &high);
val[0] = INTVAL (low);
val[1] = INTVAL (high);
}
if (GET_CODE (operands[1]) == CONST_DOUBLE)
{
if (GET_MODE (operands[1]) == DFmode)
{
REAL_VALUE_FROM_CONST_DOUBLE (rv, operands[1]);
REAL_VALUE_TO_TARGET_DOUBLE (rv, val);
}
else if (GET_MODE (operands[1]) == VOIDmode
|| GET_MODE (operands[1]) == DImode)
{
val[0] = CONST_DOUBLE_LOW (operands[1]);
val[1] = CONST_DOUBLE_HIGH (operands[1]);
}
}
 
if (GET_CODE (operands[1]) == MEM
&& reg_overlap_mentioned_p (operands[0], XEXP (operands[1], 0)))
{
rtx temp = operands[0];
 
while (GET_CODE (temp) == SUBREG)
temp = SUBREG_REG (temp);
 
gcc_assert (GET_CODE (temp) == REG);
 
if (reg_overlap_mentioned_p (gen_rtx_REG (SImode, REGNO (temp)),
XEXP (operands[1], 0)))
return \"mov %H1,%H0\;mov %L1,%L0\";
else
return \"mov %L1,%L0\;mov %H1,%H0\";
}
else if (GET_CODE (operands[1]) == MEM
&& CONSTANT_ADDRESS_P (XEXP (operands[1], 0))
&& REGNO_REG_CLASS (REGNO (operands[0])) == ADDRESS_REGS)
{
rtx xoperands[2];
 
xoperands[0] = operands[0];
xoperands[1] = XEXP (operands[1], 0);
 
output_asm_insn (\"mov %1,%L0\;mov (4,%L0),%H0\;mov (%L0),%L0\",
xoperands);
return \"\";
}
else
{
if ((GET_CODE (operands[1]) == CONST_INT
|| GET_CODE (operands[1]) == CONST_DOUBLE)
&& val[0] == 0)
{
if (REGNO_REG_CLASS (REGNO (operands[0])) == DATA_REGS)
output_asm_insn (\"clr %L0\", operands);
else
output_asm_insn (\"mov %L1,%L0\", operands);
}
else if ((GET_CODE (operands[1]) == CONST_INT
|| GET_CODE (operands[1]) == CONST_DOUBLE)
&& (REGNO_REG_CLASS (true_regnum (operands[0]))
== EXTENDED_REGS)
&& (((val[0] & 0x80) && ! (val[0] & 0xffffff00))
|| ((val[0] & 0x800000) && ! (val[0] & 0xff000000))))
output_asm_insn (\"movu %L1,%L0\", operands);
else
output_asm_insn (\"mov %L1,%L0\", operands);
 
if ((GET_CODE (operands[1]) == CONST_INT
|| GET_CODE (operands[1]) == CONST_DOUBLE)
&& val[1] == 0)
{
if (REGNO_REG_CLASS (REGNO (operands[0])) == DATA_REGS)
output_asm_insn (\"clr %H0\", operands);
else
output_asm_insn (\"mov %H1,%H0\", operands);
}
else if ((GET_CODE (operands[1]) == CONST_INT
|| GET_CODE (operands[1]) == CONST_DOUBLE)
&& val[0] == val[1])
output_asm_insn (\"mov %L0,%H0\", operands);
else if ((GET_CODE (operands[1]) == CONST_INT
|| GET_CODE (operands[1]) == CONST_DOUBLE)
&& (REGNO_REG_CLASS (true_regnum (operands[0]))
== EXTENDED_REGS)
&& (((val[1] & 0x80) && ! (val[1] & 0xffffff00))
|| ((val[1] & 0x800000) && ! (val[1] & 0xff000000))))
output_asm_insn (\"movu %H1,%H0\", operands);
else
output_asm_insn (\"mov %H1,%H0\", operands);
return \"\";
}
case 12:
return \"nop\";
case 13:
case 14:
case 15:
return \"fmov %L1, %L0\;fmov %H1, %H0\";
case 16:
if (GET_CODE (operands[1]) == MEM
&& GET_CODE (XEXP (operands[1], 0)) == CONST_INT
&& (INTVAL (XEXP (operands[1], 0)) & 7) == 0)
return \"fmov %D1, %D0\";
else
return \"fmov %L1, %L0\;fmov %H1, %H0\";
case 17:
if (GET_CODE (operands[0]) == MEM
&& GET_CODE (XEXP (operands[0], 0)) == CONST_INT
&& (INTVAL (XEXP (operands[0], 0)) & 7) == 0)
return \"fmov %D1, %D0\";
else
return \"fmov %L1, %L0\;fmov %H1, %H0\";
default:
gcc_unreachable ();
}
}"
[(set (attr "cc")
(cond
[
(ior (lt (symbol_ref "which_alternative") (const_int 2))
(eq (symbol_ref "which_alternative") (const_int 12))
) (const_string "none")
(eq (symbol_ref "which_alternative") (const_int 2)
) (const_string "clobber")
(eq (symbol_ref "which_alternative") (const_int 3)
) (if_then_else
(ne (symbol_ref "rtx_equal_p (operands[0], operands[1])")
(const_int 0)) (const_string "clobber")
(const_string "none_0hit"))
(ior (eq (symbol_ref "which_alternative") (const_int 8))
(eq (symbol_ref "which_alternative") (const_int 9))
) (if_then_else
(ne (symbol_ref "mn10300_wide_const_load_uses_clr
(operands)")
(const_int 0)) (const_string "clobber")
(const_string "none_0hit"))
] (const_string "none_0hit")))])
 
(define_expand "movdf"
[(set (match_operand:DF 0 "general_operand" "")
(match_operand:DF 1 "general_operand" ""))]
""
"
{
/* One of the ops has to be in a register */
if (!register_operand (operand1, DFmode)
&& !register_operand (operand0, DFmode))
operands[1] = copy_to_mode_reg (DFmode, operand1);
}")
 
(define_insn ""
[(set (match_operand:DF 0 "nonimmediate_operand"
"=f,dx,ax,dx,f,f,dxa,f,Q,a,dxm,dxm,axm,axm,dx,dx,ax,ax")
(match_operand:DF 1 "general_operand"
"0,0,0,G,f,dxaF,f,Q,f,G,dx,ax,dx,ax,dxFm,axFm,dxFm,axFm"))]
"register_operand (operands[0], DFmode)
|| register_operand (operands[1], DFmode)"
"*
{
long val[2];
REAL_VALUE_TYPE rv;
 
switch (which_alternative)
{
case 0:
case 1:
case 2:
return \"nop\";
 
case 3:
return \"clr %L0\;clr %H0\";
 
case 4:
case 5:
case 6:
return \"fmov %L1, %L0\;fmov %H1, %H0\";
 
case 7:
if (GET_CODE (operands[1]) == MEM
&& GET_CODE (XEXP (operands[1], 0)) == CONST_INT
&& (INTVAL (XEXP (operands[1], 0)) & 7) == 0)
return \"fmov %D1, %D0\";
else
return \"fmov %L1, %L0\;fmov %H1, %H0\";
 
case 8:
if (GET_CODE (operands[0]) == MEM
&& GET_CODE (XEXP (operands[0], 0)) == CONST_INT
&& (INTVAL (XEXP (operands[0], 0)) & 7) == 0)
return \"fmov %D1, %D0\";
else
return \"fmov %L1, %L0\;fmov %H1, %H0\";
 
case 9:
if (rtx_equal_p (operands[0], operands[1]))
return \"sub %L1,%L0\;mov %L0,%H0\";
else
return \"mov %1,%L0\;mov %L0,%H0\";
case 10:
case 11:
case 12:
case 13:
case 14:
case 15:
case 16:
case 17:
if (GET_CODE (operands[1]) == CONST_INT)
{
rtx low, high;
split_double (operands[1], &low, &high);
val[0] = INTVAL (low);
val[1] = INTVAL (high);
}
if (GET_CODE (operands[1]) == CONST_DOUBLE)
{
if (GET_MODE (operands[1]) == DFmode)
{
REAL_VALUE_FROM_CONST_DOUBLE (rv, operands[1]);
REAL_VALUE_TO_TARGET_DOUBLE (rv, val);
}
else if (GET_MODE (operands[1]) == VOIDmode
|| GET_MODE (operands[1]) == DImode)
{
val[0] = CONST_DOUBLE_LOW (operands[1]);
val[1] = CONST_DOUBLE_HIGH (operands[1]);
}
}
 
if (GET_CODE (operands[1]) == MEM
&& reg_overlap_mentioned_p (operands[0], XEXP (operands[1], 0)))
{
rtx temp = operands[0];
 
while (GET_CODE (temp) == SUBREG)
temp = SUBREG_REG (temp);
 
gcc_assert (GET_CODE (temp) == REG);
 
if (reg_overlap_mentioned_p (gen_rtx_REG (SImode, REGNO (temp)),
XEXP (operands[1], 0)))
return \"mov %H1,%H0\;mov %L1,%L0\";
else
return \"mov %L1,%L0\;mov %H1,%H0\";
}
else if (GET_CODE (operands[1]) == MEM
&& CONSTANT_ADDRESS_P (XEXP (operands[1], 0))
&& REGNO_REG_CLASS (REGNO (operands[0])) == ADDRESS_REGS)
{
rtx xoperands[2];
 
xoperands[0] = operands[0];
xoperands[1] = XEXP (operands[1], 0);
 
output_asm_insn (\"mov %1,%L0\;mov (4,%L0),%H0\;mov (%L0),%L0\",
xoperands);
return \"\";
}
else
{
if ((GET_CODE (operands[1]) == CONST_INT
|| GET_CODE (operands[1]) == CONST_DOUBLE)
&& val[0] == 0)
{
if (REGNO_REG_CLASS (REGNO (operands[0])) == DATA_REGS)
output_asm_insn (\"clr %L0\", operands);
else
output_asm_insn (\"mov %L1,%L0\", operands);
}
else if ((GET_CODE (operands[1]) == CONST_INT
|| GET_CODE (operands[1]) == CONST_DOUBLE)
&& (REGNO_REG_CLASS (true_regnum (operands[0]))
== EXTENDED_REGS)
&& (((val[0] & 0x80) && ! (val[0] & 0xffffff00))
|| ((val[0] & 0x800000) && ! (val[0] & 0xff000000))))
output_asm_insn (\"movu %L1,%L0\", operands);
else
output_asm_insn (\"mov %L1,%L0\", operands);
 
if ((GET_CODE (operands[1]) == CONST_INT
|| GET_CODE (operands[1]) == CONST_DOUBLE)
&& val[1] == 0)
{
if (REGNO_REG_CLASS (REGNO (operands[0])) == DATA_REGS)
output_asm_insn (\"clr %H0\", operands);
else
output_asm_insn (\"mov %H1,%H0\", operands);
}
else if ((GET_CODE (operands[1]) == CONST_INT
|| GET_CODE (operands[1]) == CONST_DOUBLE)
&& val[0] == val[1])
output_asm_insn (\"mov %L0,%H0\", operands);
else if ((GET_CODE (operands[1]) == CONST_INT
|| GET_CODE (operands[1]) == CONST_DOUBLE)
&& (REGNO_REG_CLASS (true_regnum (operands[0]))
== EXTENDED_REGS)
&& (((val[1] & 0x80) && ! (val[1] & 0xffffff00))
|| ((val[1] & 0x800000) && ! (val[1] & 0xff000000))))
output_asm_insn (\"movu %H1,%H0\", operands);
else
output_asm_insn (\"mov %H1,%H0\", operands);
return \"\";
}
default:
gcc_unreachable ();
}
}"
[(set (attr "cc")
(cond
[
(lt (symbol_ref "which_alternative") (const_int 3)
) (const_string "none")
(eq (symbol_ref "which_alternative") (const_int 3)
) (const_string "clobber")
(eq (symbol_ref "which_alternative") (const_int 9)
) (if_then_else
(ne (symbol_ref "rtx_equal_p (operands[0], operands[1])")
(const_int 0)) (const_string "clobber")
(const_string "none_0hit"))
(ior (eq (symbol_ref "which_alternative") (const_int 14))
(eq (symbol_ref "which_alternative") (const_int 15))
) (if_then_else
(ne (symbol_ref "mn10300_wide_const_load_uses_clr
(operands)")
(const_int 0)) (const_string "clobber")
(const_string "none_0hit"))
] (const_string "none_0hit")))])
 
 
;; ----------------------------------------------------------------------
;; TEST INSTRUCTIONS
;; ----------------------------------------------------------------------
 
;; Go ahead and define tstsi so we can eliminate redundant tst insns
;; when we start trying to optimize this port.
(define_insn "tstsi"
[(set (cc0) (match_operand:SI 0 "register_operand" "dax"))]
""
"* return output_tst (operands[0], insn);"
[(set_attr "cc" "set_znv")])
 
(define_insn ""
[(set (cc0) (zero_extend:SI (match_operand:QI 0 "memory_operand" "dx,!a")))]
"TARGET_AM33"
"* return output_tst (operands[0], insn);"
[(set_attr "cc" "set_znv")])
 
(define_insn ""
[(set (cc0) (zero_extend:SI (match_operand:QI 0 "memory_operand" "dx")))]
""
"* return output_tst (operands[0], insn);"
[(set_attr "cc" "set_znv")])
 
(define_insn ""
[(set (cc0) (zero_extend:SI (match_operand:HI 0 "memory_operand" "dx,!a")))]
"TARGET_AM33"
"* return output_tst (operands[0], insn);"
[(set_attr "cc" "set_znv")])
 
(define_insn ""
[(set (cc0) (zero_extend:SI (match_operand:HI 0 "memory_operand" "dx")))]
""
"* return output_tst (operands[0], insn);"
[(set_attr "cc" "set_znv")])
 
;; Ordinarily, the cmp instruction will set the Z bit of cc0 to 1 if
;; its operands hold equal values, but the operands of a cmp
;; instruction must be distinct registers. In the case where we'd
;; like to compare a register to itself, we can achieve this effect
;; with a btst 0,d0 instead. (This will not alter the contents of d0
;; but will have the proper effect on cc0. Using d0 is arbitrary; any
;; data register would work.)
 
;; Even though the first alternative would be preferable if it can
;; possibly match, reload must not be given the opportunity to attempt
;; to use it. It assumes that such matches can only occur when one of
;; the operands is used for input and the other for output. Since
;; this is not the case, it abort()s. Indeed, such a reload cannot be
;; possibly satisfied, so just mark the alternative with a `!', so
;; that it is not considered by reload.
 
(define_insn "cmpsi"
[(set (cc0)
(compare (match_operand:SI 0 "register_operand" "!*d*a*x,dax")
(match_operand:SI 1 "nonmemory_operand" "*0,daxi")))]
""
"@
btst 0,d0
cmp %1,%0"
[(set_attr "cc" "compare,compare")])
 
(define_insn "cmpsf"
[(set (cc0)
(compare (match_operand:SF 0 "register_operand" "f,f")
(match_operand:SF 1 "nonmemory_operand" "f,F")))]
"TARGET_AM33_2"
"fcmp %1,%0"
[(set_attr "cc" "compare,compare")])
;; ----------------------------------------------------------------------
;; ADD INSTRUCTIONS
;; ----------------------------------------------------------------------
 
(define_expand "addsi3"
[(set (match_operand:SI 0 "register_operand" "")
(plus:SI (match_operand:SI 1 "register_operand" "")
(match_operand:SI 2 "nonmemory_operand" "")))]
""
"")
 
(define_insn ""
[(set (match_operand:SI 0 "register_operand" "=dx,a,x,a,dax,!*y,!dax")
(plus:SI (match_operand:SI 1 "register_operand" "%0,0,0,0,0,0,dax")
(match_operand:SI 2 "nonmemory_operand" "J,J,L,L,daxi,i,dax")))]
"TARGET_AM33"
"*
{
switch (which_alternative)
{
case 0:
case 1:
return \"inc %0\";
case 2:
case 3:
return \"inc4 %0\";
case 4:
case 5:
return \"add %2,%0\";
case 6:
{
enum reg_class src1_class, src2_class, dst_class;
 
src1_class = REGNO_REG_CLASS (true_regnum (operands[1]));
src2_class = REGNO_REG_CLASS (true_regnum (operands[2]));
dst_class = REGNO_REG_CLASS (true_regnum (operands[0]));
/* I'm not sure if this can happen or not. Might as well be prepared
and generate the best possible code if it does happen. */
if (true_regnum (operands[0]) == true_regnum (operands[1]))
return \"add %2,%0\";
if (true_regnum (operands[0]) == true_regnum (operands[2]))
return \"add %1,%0\";
 
/* Catch cases where no extended register was used. These should be
handled just like the mn10300. */
if (src1_class != EXTENDED_REGS
&& src2_class != EXTENDED_REGS
&& dst_class != EXTENDED_REGS)
{
/* We have to copy one of the sources into the destination, then
add the other source to the destination.
 
Carefully select which source to copy to the destination; a naive
implementation will waste a byte when the source classes are
different and the destination is an address register. Selecting
the lowest cost register copy will optimize this sequence. */
if (REGNO_REG_CLASS (true_regnum (operands[1]))
== REGNO_REG_CLASS (true_regnum (operands[0])))
return \"mov %1,%0\;add %2,%0\";
return \"mov %2,%0\;add %1,%0\";
}
 
/* At least one register is an extended register. */
 
/* The three operand add instruction on the am33 is a win iff the
output register is an extended register, or if both source
registers are extended registers. */
if (dst_class == EXTENDED_REGS
|| src1_class == src2_class)
return \"add %2,%1,%0\";
 
/* It is better to copy one of the sources to the destination, then
perform a 2 address add. The destination in this case must be
an address or data register and one of the sources must be an
extended register and the remaining source must not be an extended
register.
 
The best code for this case is to copy the extended reg to the
destination, then emit a two address add. */
if (src1_class == EXTENDED_REGS)
return \"mov %1,%0\;add %2,%0\";
return \"mov %2,%0\;add %1,%0\";
}
default:
gcc_unreachable ();
}
}"
[(set_attr "cc" "set_zn,none_0hit,set_zn,none_0hit,set_zn,none_0hit,set_zn")])
 
(define_insn ""
[(set (match_operand:SI 0 "register_operand" "=dx,a,a,dax,!*y,!dax")
(plus:SI (match_operand:SI 1 "register_operand" "%0,0,0,0,0,dax")
(match_operand:SI 2 "nonmemory_operand" "J,J,L,daxi,i,dax")))]
""
"*
{
switch (which_alternative)
{
case 0:
case 1:
return \"inc %0\";
case 2:
return \"inc4 %0\";
case 3:
case 4:
return \"add %2,%0\";
case 5:
/* I'm not sure if this can happen or not. Might as well be prepared
and generate the best possible code if it does happen. */
if (true_regnum (operands[0]) == true_regnum (operands[1]))
return \"add %2,%0\";
if (true_regnum (operands[0]) == true_regnum (operands[2]))
return \"add %1,%0\";
 
/* We have to copy one of the sources into the destination, then add
the other source to the destination.
 
Carefully select which source to copy to the destination; a naive
implementation will waste a byte when the source classes are different
and the destination is an address register. Selecting the lowest
cost register copy will optimize this sequence. */
if (REGNO_REG_CLASS (true_regnum (operands[1]))
== REGNO_REG_CLASS (true_regnum (operands[0])))
return \"mov %1,%0\;add %2,%0\";
return \"mov %2,%0\;add %1,%0\";
default:
gcc_unreachable ();
}
}"
[(set_attr "cc" "set_zn,none_0hit,none_0hit,set_zn,none_0hit,set_zn")])
 
;; ----------------------------------------------------------------------
;; SUBTRACT INSTRUCTIONS
;; ----------------------------------------------------------------------
 
(define_expand "subsi3"
[(set (match_operand:SI 0 "register_operand" "")
(minus:SI (match_operand:SI 1 "register_operand" "")
(match_operand:SI 2 "nonmemory_operand" "")))]
""
"")
 
(define_insn ""
[(set (match_operand:SI 0 "register_operand" "=dax,!dax")
(minus:SI (match_operand:SI 1 "register_operand" "0,dax")
(match_operand:SI 2 "nonmemory_operand" "daxi,dax")))]
"TARGET_AM33"
"*
{
if (true_regnum (operands[0]) == true_regnum (operands[1]))
return \"sub %2,%0\";
else
{
enum reg_class src1_class, src2_class, dst_class;
 
src1_class = REGNO_REG_CLASS (true_regnum (operands[1]));
src2_class = REGNO_REG_CLASS (true_regnum (operands[2]));
dst_class = REGNO_REG_CLASS (true_regnum (operands[0]));
 
/* If no extended registers are used, then the best way to handle
this is to copy the first source operand into the destination
and emit a two address subtraction. */
if (src1_class != EXTENDED_REGS
&& src2_class != EXTENDED_REGS
&& dst_class != EXTENDED_REGS
&& true_regnum (operands[0]) != true_regnum (operands[2]))
return \"mov %1,%0\;sub %2,%0\";
return \"sub %2,%1,%0\";
}
}"
[(set_attr "cc" "set_zn")])
 
(define_insn ""
[(set (match_operand:SI 0 "register_operand" "=dax")
(minus:SI (match_operand:SI 1 "register_operand" "0")
(match_operand:SI 2 "nonmemory_operand" "daxi")))]
""
"sub %2,%0"
[(set_attr "cc" "set_zn")])
 
(define_expand "negsi2"
[(set (match_operand:SI 0 "register_operand" "")
(neg:SI (match_operand:SI 1 "register_operand" "")))]
""
"
{
rtx target = gen_reg_rtx (SImode);
 
emit_move_insn (target, const0_rtx);
emit_insn (gen_subsi3 (target, target, operands[1]));
emit_move_insn (operands[0], target);
DONE;
}")
 
;; ----------------------------------------------------------------------
;; MULTIPLY INSTRUCTIONS
;; ----------------------------------------------------------------------
 
(define_insn "mulsidi3"
[(set (match_operand:DI 0 "register_operand" "=dax")
(mult:DI (sign_extend:DI (match_operand:SI 1 "register_operand" "dax"))
(sign_extend:DI (match_operand:SI 2 "register_operand" "dax"))))]
"TARGET_AM33"
"mul %1,%2,%H0,%L0"
[(set_attr "cc" "set_zn")])
 
(define_insn "umulsidi3"
[(set (match_operand:DI 0 "register_operand" "=dax")
(mult:DI (zero_extend:DI (match_operand:SI 1 "register_operand" "dax"))
(zero_extend:DI (match_operand:SI 2 "register_operand" "dax"))))]
"TARGET_AM33"
"mulu %1,%2,%H0,%L0"
[(set_attr "cc" "set_zn")])
 
(define_expand "mulsi3"
[(set (match_operand:SI 0 "register_operand" "")
(mult:SI (match_operand:SI 1 "register_operand" "")
(match_operand:SI 2 "register_operand" "")))]
""
"")
 
(define_insn ""
[(set (match_operand:SI 0 "register_operand" "=dx,!dax")
(mult:SI (match_operand:SI 1 "register_operand" "%0,0")
(match_operand:SI 2 "nonmemory_operand" "dx,daxi")))]
"TARGET_AM33"
"*
{
if (TARGET_MULT_BUG)
return \"nop\;nop\;mul %2,%0\";
else
return \"mul %2,%0\";
}"
[(set_attr "cc" "set_zn")])
(define_insn ""
[(set (match_operand:SI 0 "register_operand" "=dx")
(mult:SI (match_operand:SI 1 "register_operand" "%0")
(match_operand:SI 2 "register_operand" "dx")))]
""
"*
{
if (TARGET_MULT_BUG)
return \"nop\;nop\;mul %2,%0\";
else
return \"mul %2,%0\";
}"
[(set_attr "cc" "set_zn")])
 
(define_insn "udivmodsi4"
[(set (match_operand:SI 0 "nonimmediate_operand" "=dx")
(udiv:SI (match_operand:SI 1 "general_operand" "0")
(match_operand:SI 2 "general_operand" "dx")))
(set (match_operand:SI 3 "nonimmediate_operand" "=&d")
(umod:SI (match_dup 1) (match_dup 2)))]
""
"*
{
output_asm_insn (\"sub %3,%3\;mov %3,mdr\", operands);
 
if (find_reg_note (insn, REG_UNUSED, operands[3]))
return \"divu %2,%0\";
else
return \"divu %2,%0\;mov mdr,%3\";
}"
[(set_attr "cc" "set_zn")])
 
(define_insn "divmodsi4"
[(set (match_operand:SI 0 "nonimmediate_operand" "=dx")
(div:SI (match_operand:SI 1 "general_operand" "0")
(match_operand:SI 2 "general_operand" "dx")))
(set (match_operand:SI 3 "nonimmediate_operand" "=d")
(mod:SI (match_dup 1) (match_dup 2)))]
""
"*
{
if (find_reg_note (insn, REG_UNUSED, operands[3]))
return \"ext %0\;div %2,%0\";
else
return \"ext %0\;div %2,%0\;mov mdr,%3\";
}"
[(set_attr "cc" "set_zn")])
 
;; ----------------------------------------------------------------------
;; AND INSTRUCTIONS
;; ----------------------------------------------------------------------
 
(define_expand "andsi3"
[(set (match_operand:SI 0 "register_operand" "")
(and:SI (match_operand:SI 1 "register_operand" "")
(match_operand:SI 2 "nonmemory_operand" "")))]
""
"")
 
(define_insn ""
[(set (match_operand:SI 0 "register_operand" "=dx,dx,!dax")
(and:SI (match_operand:SI 1 "register_operand" "%0,0,dax")
(match_operand:SI 2 "nonmemory_operand" "N,dxi,dax")))]
"TARGET_AM33"
"*
{
if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) == 0xff)
return \"extbu %0\";
if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) == 0xffff)
return \"exthu %0\";
if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) == 0x7fffffff)
return \"add %0,%0\;lsr 1,%0\";
if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) == 0x3fffffff)
return \"asl2 %0\;lsr 2,%0\";
if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) == 0x1fffffff)
return \"add %0,%0\;asl2 %0\;lsr 3,%0\";
if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) == 0x0fffffff)
return \"asl2 %0\;asl2 %0\;lsr 4,%0\";
if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) == 0xfffffffe)
return \"lsr 1,%0\;add %0,%0\";
if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) == 0xfffffffc)
return \"lsr 2,%0\;asl2 %0\";
if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) == 0xfffffff8)
return \"lsr 3,%0\;add %0,%0\;asl2 %0\";
if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) == 0xfffffff0)
return \"lsr 4,%0\;asl2 %0\;asl2 %0\";
if (REG_P (operands[2]) && REG_P (operands[1])
&& true_regnum (operands[0]) != true_regnum (operands[1])
&& true_regnum (operands[0]) != true_regnum (operands[2])
&& REGNO_REG_CLASS (true_regnum (operands[0])) == DATA_REGS
&& REGNO_REG_CLASS (true_regnum (operands[1])) == DATA_REGS
&& REGNO_REG_CLASS (true_regnum (operands[2])) == DATA_REGS)
return \"mov %1,%0\;and %2,%0\";
if (REG_P (operands[2]) && REG_P (operands[1])
&& true_regnum (operands[0]) != true_regnum (operands[1])
&& true_regnum (operands[0]) != true_regnum (operands[2]))
return \"and %1,%2,%0\";
if (REG_P (operands[2]) && REG_P (operands[0])
&& true_regnum (operands[2]) == true_regnum (operands[0]))
return \"and %1,%0\";
return \"and %2,%0\";
}"
[(set (attr "cc")
(cond
[
(eq (symbol_ref "which_alternative") (const_int 0)
) (const_string "none_0hit")
(ne (symbol_ref "GET_CODE (operands[2]) == CONST_INT
&& (INTVAL (operands[2]) == 0x7fffffff
|| INTVAL (operands[2]) == 0x3fffffff
|| INTVAL (operands[2]) == 0x1fffffff
|| INTVAL (operands[2]) == 0x0fffffff
|| INTVAL (operands[2]) == 0xfffffffe
|| INTVAL (operands[2]) == 0xfffffffc
|| INTVAL (operands[2]) == 0xfffffff8
|| INTVAL (operands[2]) == 0xfffffff0)")
(const_int 0)) (const_string "set_zn")
] (const_string "set_znv")))])
 
(define_insn ""
[(set (match_operand:SI 0 "register_operand" "=dx,dx")
(and:SI (match_operand:SI 1 "register_operand" "%0,0")
(match_operand:SI 2 "nonmemory_operand" "N,dxi")))]
""
"*
{
if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) == 0xff)
return \"extbu %0\";
if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) == 0xffff)
return \"exthu %0\";
if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) == 0x7fffffff)
return \"add %0,%0\;lsr 1,%0\";
if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) == 0x3fffffff)
return \"asl2 %0\;lsr 2,%0\";
if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) == 0x1fffffff)
return \"add %0,%0\;asl2 %0\;lsr 3,%0\";
if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) == 0x0fffffff)
return \"asl2 %0\;asl2 %0\;lsr 4,%0\";
if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) == 0xfffffffe)
return \"lsr 1,%0\;add %0,%0\";
if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) == 0xfffffffc)
return \"lsr 2,%0\;asl2 %0\";
if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) == 0xfffffff8)
return \"lsr 3,%0\;add %0,%0\;asl2 %0\";
if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) == 0xfffffff0)
return \"lsr 4,%0\;asl2 %0\;asl2 %0\";
return \"and %2,%0\";
}"
[(set (attr "cc")
(cond
[
(eq (symbol_ref "which_alternative") (const_int 0)
) (const_string "none_0hit")
;; Shifts don't set the V flag, but bitwise operations clear
;; it (which correctly reflects the absence of overflow in a
;; compare-with-zero that might follow). As for the
;; 0xfffffffe case, the add may overflow, so we can't use the
;; V flag.
(ne (symbol_ref "GET_CODE (operands[2]) == CONST_INT
&& (INTVAL (operands[2]) == 0x7fffffff
|| INTVAL (operands[2]) == 0x3fffffff
|| INTVAL (operands[2]) == 0x1fffffff
|| INTVAL (operands[2]) == 0x0fffffff
|| INTVAL (operands[2]) == 0xfffffffe
|| INTVAL (operands[2]) == 0xfffffffc
|| INTVAL (operands[2]) == 0xfffffff8
|| INTVAL (operands[2]) == 0xfffffff0)")
(const_int 0)) (const_string "set_zn")
] (const_string "set_znv")))])
 
;; ----------------------------------------------------------------------
;; OR INSTRUCTIONS
;; ----------------------------------------------------------------------
 
(define_expand "iorsi3"
[(set (match_operand:SI 0 "register_operand" "")
(ior:SI (match_operand:SI 1 "register_operand" "")
(match_operand:SI 2 "nonmemory_operand" "")))]
""
"")
 
(define_insn ""
[(set (match_operand:SI 0 "register_operand" "=dx,!dax")
(ior:SI (match_operand:SI 1 "register_operand" "%0,dax")
(match_operand:SI 2 "nonmemory_operand" "dxi,dax")))]
"TARGET_AM33"
"*
{
if (REG_P (operands[2]) && REG_P (operands[1])
&& true_regnum (operands[0]) != true_regnum (operands[1])
&& true_regnum (operands[0]) != true_regnum (operands[2])
&& REGNO_REG_CLASS (true_regnum (operands[0])) == DATA_REGS
&& REGNO_REG_CLASS (true_regnum (operands[1])) == DATA_REGS
&& REGNO_REG_CLASS (true_regnum (operands[2])) == DATA_REGS)
return \"mov %1,%0\;or %2,%0\";
if (REG_P (operands[2]) && REG_P (operands[1])
&& true_regnum (operands[0]) != true_regnum (operands[1])
&& true_regnum (operands[0]) != true_regnum (operands[2]))
return \"or %1,%2,%0\";
if (REG_P (operands[2]) && REG_P (operands[0])
&& true_regnum (operands[2]) == true_regnum (operands[0]))
return \"or %1,%0\";
return \"or %2,%0\";
}"
[(set_attr "cc" "set_znv")])
 
(define_insn ""
[(set (match_operand:SI 0 "register_operand" "=dx")
(ior:SI (match_operand:SI 1 "register_operand" "%0")
(match_operand:SI 2 "nonmemory_operand" "dxi")))]
""
"or %2,%0"
[(set_attr "cc" "set_znv")])
 
;; ----------------------------------------------------------------------
;; XOR INSTRUCTIONS
;; ----------------------------------------------------------------------
 
(define_expand "xorsi3"
[(set (match_operand:SI 0 "register_operand" "")
(xor:SI (match_operand:SI 1 "register_operand" "")
(match_operand:SI 2 "nonmemory_operand" "")))]
""
"")
 
(define_insn ""
[(set (match_operand:SI 0 "register_operand" "=dx,!dax")
(xor:SI (match_operand:SI 1 "register_operand" "%0,dax")
(match_operand:SI 2 "nonmemory_operand" "dxi,dax")))]
"TARGET_AM33"
"*
{
if (REG_P (operands[2]) && REG_P (operands[1])
&& true_regnum (operands[0]) != true_regnum (operands[1])
&& true_regnum (operands[0]) != true_regnum (operands[2])
&& REGNO_REG_CLASS (true_regnum (operands[0])) == DATA_REGS
&& REGNO_REG_CLASS (true_regnum (operands[1])) == DATA_REGS
&& REGNO_REG_CLASS (true_regnum (operands[2])) == DATA_REGS)
return \"mov %1,%0\;xor %2,%0\";
if (REG_P (operands[2]) && REG_P (operands[1])
&& true_regnum (operands[0]) != true_regnum (operands[1])
&& true_regnum (operands[0]) != true_regnum (operands[2]))
return \"xor %1,%2,%0\";
if (REG_P (operands[2]) && REG_P (operands[0])
&& true_regnum (operands[2]) == true_regnum (operands[0]))
return \"xor %1,%0\";
return \"xor %2,%0\";
}"
[(set_attr "cc" "set_znv")])
 
(define_insn ""
[(set (match_operand:SI 0 "register_operand" "=dx")
(xor:SI (match_operand:SI 1 "register_operand" "%0")
(match_operand:SI 2 "nonmemory_operand" "dxi")))]
""
"xor %2,%0"
[(set_attr "cc" "set_znv")])
 
;; ----------------------------------------------------------------------
;; NOT INSTRUCTIONS
;; ----------------------------------------------------------------------
 
(define_expand "one_cmplsi2"
[(set (match_operand:SI 0 "register_operand" "")
(not:SI (match_operand:SI 1 "register_operand" "")))]
""
"")
 
(define_insn ""
[(set (match_operand:SI 0 "register_operand" "=dx,!dax")
(not:SI (match_operand:SI 1 "register_operand" "0,0")))]
"TARGET_AM33"
"not %0"
[(set_attr "cc" "set_znv")])
 
(define_insn ""
[(set (match_operand:SI 0 "register_operand" "=dx")
(not:SI (match_operand:SI 1 "register_operand" "0")))]
""
"not %0"
[(set_attr "cc" "set_znv")])
;; -----------------------------------------------------------------
;; BIT FIELDS
;; -----------------------------------------------------------------
 
 
;; These set/clear memory in byte sized chunks.
;;
;; They are no smaller/faster than loading the value into a register
;; and storing the register, but they don't need a scratch register
;; which may allow for better code generation.
(define_insn ""
[(set (match_operand:QI 0 "nonimmediate_operand" "=R,d") (const_int 0))]
""
"@
bclr 255,%A0
clr %0"
[(set_attr "cc" "clobber")])
 
(define_insn ""
[(set (match_operand:QI 0 "nonimmediate_operand" "=R,d") (const_int -1))]
""
"@
bset 255,%A0
mov -1,%0"
[(set_attr "cc" "clobber,none_0hit")])
 
(define_insn ""
[(set (match_operand:QI 0 "nonimmediate_operand" "+R,d")
(subreg:QI
(and:SI (subreg:SI (match_dup 0) 0)
(match_operand:SI 1 "const_int_operand" "i,i")) 0))]
""
"@
bclr %N1,%A0
and %1,%0"
[(set_attr "cc" "clobber,set_znv")])
 
(define_insn ""
[(set (match_operand:QI 0 "memory_operand" "=R,T")
(and:QI
(match_dup 0)
(not:QI (match_operand:QI 1 "nonmemory_operand" "i,d"))))]
""
"@
bclr %U1,%A0
bclr %1,%0"
[(set_attr "cc" "clobber,clobber")])
 
(define_insn ""
[(set (match_operand:QI 0 "nonimmediate_operand" "+R,d")
(subreg:QI
(ior:SI (subreg:SI (match_dup 0) 0)
(match_operand:SI 1 "const_int_operand" "i,i")) 0))]
""
"@
bset %U1,%A0
or %1,%0"
[(set_attr "cc" "clobber,set_znv")])
 
(define_expand "iorqi3"
[(set (match_operand:QI 0 "nonimmediate_operand" "")
(ior:QI (match_operand:QI 1 "nonimmediate_operand" "")
(match_operand:QI 2 "nonmemory_operand" "")))]
""
"")
 
(define_insn ""
[(set (match_operand:QI 0 "nonimmediate_operand" "=R,T,r")
(ior:QI (match_operand:QI 1 "nonimmediate_operand" "%0,0,0")
;; This constraint should really be nonmemory_operand,
;; but making it general_operand, along with the
;; condition that not both input operands are MEMs, it
;; here helps combine do a better job.
(match_operand:QI 2 "general_operand" "i,d,ir")))]
"TARGET_AM33 &&
(GET_CODE (operands[2]) != MEM || GET_CODE (operands[1]) != MEM)"
"@
bset %U2,%A0
bset %2,%0
or %2,%0"
[(set_attr "cc" "clobber,clobber,set_znv")])
 
(define_insn ""
[(set (match_operand:QI 0 "nonimmediate_operand" "=R,T,d")
(ior:QI (match_operand:QI 1 "nonimmediate_operand" "%0,0,0")
;; This constraint should really be nonmemory_operand,
;; but making it general_operand, along with the
;; condition that not both input operands are MEMs, it
;; here helps combine do a better job.
(match_operand:QI 2 "general_operand" "i,d,id")))]
"GET_CODE (operands[2]) != MEM || GET_CODE (operands[1]) != MEM"
"@
bset %U2,%A0
bset %2,%0
or %2,%0"
[(set_attr "cc" "clobber,clobber,set_znv")])
 
(define_insn ""
[(set (cc0)
(zero_extract:SI (match_operand:SI 0 "register_operand" "dx")
(match_operand 1 "const_int_operand" "")
(match_operand 2 "const_int_operand" "")))]
""
"*
{
int len = INTVAL (operands[1]);
int bit = INTVAL (operands[2]);
int mask = 0;
rtx xoperands[2];
 
while (len > 0)
{
mask |= (1 << bit);
bit++;
len--;
}
 
xoperands[0] = operands[0];
xoperands[1] = GEN_INT (trunc_int_for_mode (mask, SImode));
output_asm_insn (\"btst %1,%0\", xoperands);
return \"\";
}"
[(set_attr "cc" "clobber")])
 
(define_insn ""
[(set (cc0)
(zero_extract:SI (match_operand:QI 0 "general_operand" "R,dx")
(match_operand 1 "const_int_operand" "")
(match_operand 2 "const_int_operand" "")))]
"mask_ok_for_mem_btst (INTVAL (operands[1]), INTVAL (operands[2]))"
"*
{
int len = INTVAL (operands[1]);
int bit = INTVAL (operands[2]);
int mask = 0;
rtx xoperands[2];
 
while (len > 0)
{
mask |= (1 << bit);
bit++;
len--;
}
 
/* If the source operand is not a reg (i.e. it is memory), then extract the
bits from mask that we actually want to test. Note that the mask will
never cross a byte boundary. */
if (!REG_P (operands[0]))
{
if (mask & 0xff)
mask = mask & 0xff;
else if (mask & 0xff00)
mask = (mask >> 8) & 0xff;
else if (mask & 0xff0000)
mask = (mask >> 16) & 0xff;
else if (mask & 0xff000000)
mask = (mask >> 24) & 0xff;
}
xoperands[0] = operands[0];
xoperands[1] = GEN_INT (trunc_int_for_mode (mask, SImode));
if (GET_CODE (operands[0]) == REG)
output_asm_insn (\"btst %1,%0\", xoperands);
else
output_asm_insn (\"btst %U1,%A0\", xoperands);
return \"\";
}"
[(set_attr "cc" "clobber")])
 
(define_insn ""
[(set (cc0) (and:SI (match_operand:SI 0 "register_operand" "dx")
(match_operand:SI 1 "const_int_operand" "")))]
""
"btst %1,%0"
[(set_attr "cc" "clobber")])
 
(define_insn ""
[(set (cc0)
(and:SI
(subreg:SI (match_operand:QI 0 "general_operand" "R,dx") 0)
(match_operand:SI 1 "const_8bit_operand" "")))]
""
"@
btst %U1,%A0
btst %1,%0"
[(set_attr "cc" "clobber")])
 
;; ----------------------------------------------------------------------
;; JUMP INSTRUCTIONS
;; ----------------------------------------------------------------------
 
;; Conditional jump instructions
 
(define_expand "ble"
[(set (pc)
(if_then_else (le (cc0)
(const_int 0))
(label_ref (match_operand 0 "" ""))
(pc)))]
""
"")
 
(define_expand "bleu"
[(set (pc)
(if_then_else (leu (cc0)
(const_int 0))
(label_ref (match_operand 0 "" ""))
(pc)))]
""
"")
 
(define_expand "bge"
[(set (pc)
(if_then_else (ge (cc0)
(const_int 0))
(label_ref (match_operand 0 "" ""))
(pc)))]
""
"")
 
(define_expand "bgeu"
[(set (pc)
(if_then_else (geu (cc0)
(const_int 0))
(label_ref (match_operand 0 "" ""))
(pc)))]
""
"")
 
(define_expand "blt"
[(set (pc)
(if_then_else (lt (cc0)
(const_int 0))
(label_ref (match_operand 0 "" ""))
(pc)))]
""
"")
 
(define_expand "bltu"
[(set (pc)
(if_then_else (ltu (cc0)
(const_int 0))
(label_ref (match_operand 0 "" ""))
(pc)))]
""
"")
 
(define_expand "bgt"
[(set (pc)
(if_then_else (gt (cc0)
(const_int 0))
(label_ref (match_operand 0 "" ""))
(pc)))]
""
"")
 
(define_expand "bgtu"
[(set (pc)
(if_then_else (gtu (cc0)
(const_int 0))
(label_ref (match_operand 0 "" ""))
(pc)))]
""
"")
 
(define_expand "beq"
[(set (pc)
(if_then_else (eq (cc0)
(const_int 0))
(label_ref (match_operand 0 "" ""))
(pc)))]
""
"")
 
(define_expand "bne"
[(set (pc)
(if_then_else (ne (cc0)
(const_int 0))
(label_ref (match_operand 0 "" ""))
(pc)))]
""
"")
 
(define_insn ""
[(set (pc)
(if_then_else (match_operator 1 "comparison_operator"
[(cc0) (const_int 0)])
(label_ref (match_operand 0 "" ""))
(pc)))]
""
"*
{
if (cc_status.mdep.fpCC)
return \"fb%b1 %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))
return 0;
return \"b%b1 %0\";
}"
[(set_attr "cc" "none")])
 
(define_insn ""
[(set (pc)
(if_then_else (match_operator 1 "comparison_operator"
[(cc0) (const_int 0)])
(pc)
(label_ref (match_operand 0 "" ""))))]
""
"*
{
if (cc_status.mdep.fpCC)
return \"fb%B1 %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))
return 0;
return \"b%B1 %0\";
}"
[(set_attr "cc" "none")])
 
;; Unconditional and other jump instructions.
 
(define_insn "jump"
[(set (pc)
(label_ref (match_operand 0 "" "")))]
""
"jmp %l0"
[(set_attr "cc" "none")])
 
(define_insn "indirect_jump"
[(set (pc) (match_operand:SI 0 "register_operand" "a"))]
""
"jmp (%0)"
[(set_attr "cc" "none")])
 
(define_expand "builtin_setjmp_receiver"
[(match_operand 0 "" "")]
"flag_pic"
"
{
if (flag_pic)
emit_insn (gen_GOTaddr2picreg ());
 
DONE;
}")
 
(define_expand "casesi"
[(match_operand:SI 0 "register_operand" "")
(match_operand:SI 1 "immediate_operand" "")
(match_operand:SI 2 "immediate_operand" "")
(match_operand 3 "" "") (match_operand 4 "" "")]
""
"
{
rtx table = gen_reg_rtx (SImode);
rtx index = gen_reg_rtx (SImode);
rtx addr = gen_reg_rtx (Pmode);
 
emit_move_insn (table, gen_rtx_LABEL_REF (VOIDmode, operands[3]));
emit_move_insn (index, plus_constant (operands[0], - INTVAL (operands[1])));
emit_insn (gen_cmpsi (index, operands[2]));
emit_jump_insn (gen_bgtu (operands[4]));
emit_move_insn (index, gen_rtx_ASHIFT (SImode, index, const2_rtx));
emit_move_insn (addr, gen_rtx_MEM (SImode,
gen_rtx_PLUS (SImode, table, index)));
if (flag_pic)
emit_move_insn (addr, gen_rtx_PLUS (SImode, addr, table));
 
emit_jump_insn (gen_tablejump (addr, operands[3]));
DONE;
}")
 
(define_insn "tablejump"
[(set (pc) (match_operand:SI 0 "register_operand" "a"))
(use (label_ref (match_operand 1 "" "")))]
""
"jmp (%0)"
[(set_attr "cc" "none")])
 
;; Call subroutine with no return value.
 
(define_expand "call"
[(call (match_operand:QI 0 "general_operand" "")
(match_operand:SI 1 "general_operand" ""))]
""
"
{
if (flag_pic && GET_CODE (XEXP (operands[0], 0)) == SYMBOL_REF)
{
if (MN10300_GLOBAL_P (XEXP (operands[0], 0)))
{
/* The PLT code won't run on AM30, but then, there's no
shared library support for AM30 either, so we just assume
the linker is going to adjust all @PLT relocs to the
actual symbols. */
emit_insn (gen_rtx_USE (VOIDmode, pic_offset_table_rtx));
XEXP (operands[0], 0) = gen_sym2PLT (XEXP (operands[0], 0));
}
else
XEXP (operands[0], 0) = gen_sym2PIC (XEXP (operands[0], 0));
}
if (! call_address_operand (XEXP (operands[0], 0), VOIDmode))
XEXP (operands[0], 0) = force_reg (SImode, XEXP (operands[0], 0));
emit_call_insn (gen_call_internal (XEXP (operands[0], 0), operands[1]));
DONE;
}")
 
(define_insn "call_internal"
[(call (mem:QI (match_operand:SI 0 "call_address_operand" "aS"))
(match_operand:SI 1 "general_operand" "g"))]
""
"*
{
if (REG_P (operands[0]))
return \"calls %C0\";
else
return \"call %C0,[],0\";
}"
[(set_attr "cc" "clobber")])
 
;; Call subroutine, returning value in operand 0
;; (which must be a hard register).
 
(define_expand "call_value"
[(set (match_operand 0 "" "")
(call (match_operand:QI 1 "general_operand" "")
(match_operand:SI 2 "general_operand" "")))]
""
"
{
if (flag_pic && GET_CODE (XEXP (operands[1], 0)) == SYMBOL_REF)
{
if (MN10300_GLOBAL_P (XEXP (operands[1], 0)))
{
/* The PLT code won't run on AM30, but then, there's no
shared library support for AM30 either, so we just assume
the linker is going to adjust all @PLT relocs to the
actual symbols. */
emit_insn (gen_rtx_USE (VOIDmode, pic_offset_table_rtx));
XEXP (operands[1], 0) = gen_sym2PLT (XEXP (operands[1], 0));
}
else
XEXP (operands[1], 0) = gen_sym2PIC (XEXP (operands[1], 0));
}
if (! call_address_operand (XEXP (operands[1], 0), VOIDmode))
XEXP (operands[1], 0) = force_reg (SImode, XEXP (operands[1], 0));
emit_call_insn (gen_call_value_internal (operands[0],
XEXP (operands[1], 0),
operands[2]));
DONE;
}")
 
(define_insn "call_value_internal"
[(set (match_operand 0 "" "=dax")
(call (mem:QI (match_operand:SI 1 "call_address_operand" "aS"))
(match_operand:SI 2 "general_operand" "g")))]
""
"*
{
if (REG_P (operands[1]))
return \"calls %C1\";
else
return \"call %C1,[],0\";
}"
[(set_attr "cc" "clobber")])
 
(define_expand "untyped_call"
[(parallel [(call (match_operand 0 "" "")
(const_int 0))
(match_operand 1 "" "")
(match_operand 2 "" "")])]
""
"
{
int i;
 
emit_call_insn (gen_call (operands[0], const0_rtx));
 
for (i = 0; i < XVECLEN (operands[2], 0); i++)
{
rtx set = XVECEXP (operands[2], 0, i);
emit_move_insn (SET_DEST (set), SET_SRC (set));
}
DONE;
}")
 
(define_insn "nop"
[(const_int 0)]
""
"nop"
[(set_attr "cc" "none")])
;; ----------------------------------------------------------------------
;; EXTEND INSTRUCTIONS
;; ----------------------------------------------------------------------
 
(define_expand "zero_extendqisi2"
[(set (match_operand:SI 0 "general_operand" "")
(zero_extend:SI
(match_operand:QI 1 "general_operand" "")))]
""
"")
 
(define_insn ""
[(set (match_operand:SI 0 "nonimmediate_operand" "=dx,dx,dx,!dax,!dax,!dax")
(zero_extend:SI
(match_operand:QI 1 "general_operand" "0,dax,m,0,dax,m")))]
"TARGET_AM33"
"@
extbu %0
mov %1,%0\;extbu %0
movbu %1,%0
extbu %0
mov %1,%0\;extbu %0
movbu %1,%0"
[(set_attr "cc" "none_0hit")])
 
(define_insn ""
[(set (match_operand:SI 0 "nonimmediate_operand" "=dx,dx,dx")
(zero_extend:SI
(match_operand:QI 1 "general_operand" "0,d,m")))]
""
"@
extbu %0
mov %1,%0\;extbu %0
movbu %1,%0"
[(set_attr "cc" "none_0hit")])
 
(define_expand "zero_extendhisi2"
[(set (match_operand:SI 0 "general_operand" "")
(zero_extend:SI
(match_operand:HI 1 "general_operand" "")))]
""
"")
 
(define_insn ""
[(set (match_operand:SI 0 "nonimmediate_operand" "=dx,dx,dx,!dax,!dax,!dax")
(zero_extend:SI
(match_operand:HI 1 "general_operand" "0,dax,m,0,dax,m")))]
"TARGET_AM33"
"@
exthu %0
mov %1,%0\;exthu %0
movhu %1,%0
exthu %0
mov %1,%0\;exthu %0
movhu %1,%0"
[(set_attr "cc" "none_0hit")])
 
(define_insn ""
[(set (match_operand:SI 0 "nonimmediate_operand" "=dx,dx,dx")
(zero_extend:SI
(match_operand:HI 1 "general_operand" "0,dx,m")))]
""
"@
exthu %0
mov %1,%0\;exthu %0
movhu %1,%0"
[(set_attr "cc" "none_0hit")])
 
;;- sign extension instructions
 
(define_expand "extendqisi2"
[(set (match_operand:SI 0 "general_operand" "")
(sign_extend:SI
(match_operand:QI 1 "general_operand" "")))]
""
"")
 
(define_insn ""
[(set (match_operand:SI 0 "nonimmediate_operand" "=dx,dx,!dax,!dax")
(sign_extend:SI
(match_operand:QI 1 "general_operand" "0,dx,0,dax")))]
"TARGET_AM33"
"@
extb %0
mov %1,%0\;extb %0
extb %0
mov %1,%0\;extb %0"
[(set_attr "cc" "none_0hit")])
 
(define_insn ""
[(set (match_operand:SI 0 "nonimmediate_operand" "=dx,dx")
(sign_extend:SI
(match_operand:QI 1 "general_operand" "0,dx")))]
""
"@
extb %0
mov %1,%0\;extb %0"
[(set_attr "cc" "none_0hit")])
 
(define_expand "extendhisi2"
[(set (match_operand:SI 0 "general_operand" "")
(sign_extend:SI
(match_operand:HI 1 "general_operand" "")))]
""
"")
 
(define_insn ""
[(set (match_operand:SI 0 "nonimmediate_operand" "=dx,dx,!dax,!dax")
(sign_extend:SI
(match_operand:HI 1 "general_operand" "0,dax,0,dax")))]
"TARGET_AM33"
"@
exth %0
mov %1,%0\;exth %0
exth %0
mov %1,%0\;exth %0"
[(set_attr "cc" "none_0hit")])
 
(define_insn ""
[(set (match_operand:SI 0 "nonimmediate_operand" "=dx,dx")
(sign_extend:SI
(match_operand:HI 1 "general_operand" "0,dx")))]
""
"@
exth %0
mov %1,%0\;exth %0"
[(set_attr "cc" "none_0hit")])
;; ----------------------------------------------------------------------
;; 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" "")))]
""
"")
 
(define_insn ""
[(set (match_operand:SI 0 "register_operand" "=dax,dx,!dax")
(ashift:SI
(match_operand:SI 1 "register_operand" "0,0,dax")
(match_operand:QI 2 "nonmemory_operand" "J,dxi,dax")))]
"TARGET_AM33"
"*
{
if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) == 1)
return \"add %0,%0\";
 
if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) == 2)
return \"asl2 %0\";
 
if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) == 3
&& REGNO_REG_CLASS (true_regnum (operands[0])) == DATA_REGS)
return \"asl2 %0\;add %0,%0\";
 
if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) == 4
&& REGNO_REG_CLASS (true_regnum (operands[0])) == DATA_REGS)
return \"asl2 %0\;asl2 %0\";
 
if (true_regnum (operands[1]) == true_regnum (operands[0]))
return \"asl %S2,%0\";
 
if (REGNO_REG_CLASS (true_regnum (operands[0])) == DATA_REGS
&& REGNO_REG_CLASS (true_regnum (operands[1])) == DATA_REGS
&& true_regnum (operands[0]) != true_regnum (operands[2]))
return \"mov %1,%0\;asl %S2,%0\";
return \"asl %2,%1,%0\";
}"
[(set_attr "cc" "set_zn")])
 
(define_insn ""
[(set (match_operand:SI 0 "register_operand" "=dax,dx,dx,dx,dx")
(ashift:SI
(match_operand:SI 1 "register_operand" "0,0,0,0,0")
(match_operand:QI 2 "nonmemory_operand" "J,K,M,L,dxi")))]
""
"@
add %0,%0
asl2 %0
asl2 %0\;add %0,%0
asl2 %0\;asl2 %0
asl %S2,%0"
[(set_attr "cc" "set_zn")])
 
(define_expand "lshrsi3"
[(set (match_operand:SI 0 "register_operand" "")
(lshiftrt:SI
(match_operand:SI 1 "register_operand" "")
(match_operand:QI 2 "nonmemory_operand" "")))]
""
"")
 
(define_insn ""
[(set (match_operand:SI 0 "register_operand" "=dx,!dax")
(lshiftrt:SI
(match_operand:SI 1 "register_operand" "0,dax")
(match_operand:QI 2 "nonmemory_operand" "dxi,dax")))]
"TARGET_AM33"
"*
{
if (true_regnum (operands[1]) == true_regnum (operands[0]))
return \"lsr %S2,%0\";
 
if (REGNO_REG_CLASS (true_regnum (operands[0])) == DATA_REGS
&& REGNO_REG_CLASS (true_regnum (operands[1])) == DATA_REGS
&& true_regnum (operands[0]) != true_regnum (operands[2]))
return \"mov %1,%0\;lsr %S2,%0\";
return \"lsr %2,%1,%0\";
}"
[(set_attr "cc" "set_zn")])
 
(define_insn ""
[(set (match_operand:SI 0 "register_operand" "=dx")
(lshiftrt:SI
(match_operand:SI 1 "register_operand" "0")
(match_operand:QI 2 "nonmemory_operand" "dxi")))]
""
"lsr %S2,%0"
[(set_attr "cc" "set_zn")])
 
(define_expand "ashrsi3"
[(set (match_operand:SI 0 "register_operand" "")
(ashiftrt:SI
(match_operand:SI 1 "register_operand" "")
(match_operand:QI 2 "nonmemory_operand" "")))]
""
"")
 
(define_insn ""
[(set (match_operand:SI 0 "register_operand" "=dx,!dax")
(ashiftrt:SI
(match_operand:SI 1 "register_operand" "0,dax")
(match_operand:QI 2 "nonmemory_operand" "dxi,dax")))]
"TARGET_AM33"
"*
{
if (true_regnum (operands[1]) == true_regnum (operands[0]))
return \"asr %S2,%0\";
 
if (REGNO_REG_CLASS (true_regnum (operands[0])) == DATA_REGS
&& REGNO_REG_CLASS (true_regnum (operands[1])) == DATA_REGS
&& true_regnum (operands[0]) != true_regnum (operands[2]))
return \"mov %1,%0\;asr %S2,%0\";
return \"asr %2,%1,%0\";
}"
[(set_attr "cc" "set_zn")])
 
(define_insn ""
[(set (match_operand:SI 0 "register_operand" "=dx")
(ashiftrt:SI
(match_operand:SI 1 "register_operand" "0")
(match_operand:QI 2 "nonmemory_operand" "dxi")))]
""
"asr %S2,%0"
[(set_attr "cc" "set_zn")])
 
;; ----------------------------------------------------------------------
;; FP INSTRUCTIONS
;; ----------------------------------------------------------------------
;;
;; The mn103 series does not have floating point instructions, but since
;; FP values are held in integer regs, we can clear the high bit easily
;; which gives us an efficient inline floating point absolute value.
;;
;; Similarly for negation of a FP value.
;;
 
(define_expand "absdf2"
[(set (match_operand:DF 0 "register_operand" "")
(abs:DF (match_operand:DF 1 "register_operand" "")))]
""
"
{
rtx target, result, insns;
 
start_sequence ();
target = operand_subword (operands[0], 1, 1, DFmode);
result = expand_binop (SImode, and_optab,
operand_subword_force (operands[1], 1, DFmode),
GEN_INT (0x7fffffff), target, 0, OPTAB_WIDEN);
 
gcc_assert (result);
 
if (result != target)
emit_move_insn (result, target);
 
emit_move_insn (operand_subword (operands[0], 0, 1, DFmode),
operand_subword_force (operands[1], 0, DFmode));
 
insns = get_insns ();
end_sequence ();
 
emit_no_conflict_block (insns, operands[0], operands[1], 0, 0);
DONE;
}")
 
(define_expand "abssf2"
[(set (match_operand:SF 0 "register_operand" "")
(abs:SF (match_operand:SF 1 "register_operand" "")))]
""
"
{
rtx result;
rtx target;
 
if (TARGET_AM33_2)
{
emit_insn (gen_abssf2_am33_2 (operands[0], operands[1]));
DONE;
}
 
target = operand_subword_force (operands[0], 0, SFmode);
result = expand_binop (SImode, and_optab,
operand_subword_force (operands[1], 0, SFmode),
GEN_INT (0x7fffffff), target, 0, OPTAB_WIDEN);
gcc_assert (result);
 
if (result != target)
emit_move_insn (result, target);
 
/* Make a place for REG_EQUAL. */
emit_move_insn (operands[0], operands[0]);
DONE;
}")
 
 
(define_insn "abssf2_am33_2"
[(set (match_operand:SF 0 "register_operand" "=f,f")
(abs:SF (match_operand:SF 1 "register_operand" "0,?f")))]
"TARGET_AM33_2"
"@
fabs %0
fabs %1, %0"
[(set_attr "cc" "none_0hit")])
 
(define_expand "negdf2"
[(set (match_operand:DF 0 "register_operand" "")
(neg:DF (match_operand:DF 1 "register_operand" "")))]
""
"
{
rtx target, result, insns;
 
start_sequence ();
target = operand_subword (operands[0], 1, 1, DFmode);
result = expand_binop (SImode, xor_optab,
operand_subword_force (operands[1], 1, DFmode),
GEN_INT (trunc_int_for_mode (0x80000000, SImode)),
target, 0, OPTAB_WIDEN);
 
gcc_assert (result);
 
if (result != target)
emit_move_insn (result, target);
 
emit_move_insn (operand_subword (operands[0], 0, 1, DFmode),
operand_subword_force (operands[1], 0, DFmode));
 
insns = get_insns ();
end_sequence ();
 
emit_no_conflict_block (insns, operands[0], operands[1], 0, 0);
DONE;
}")
 
(define_expand "negsf2"
[(set (match_operand:SF 0 "register_operand" "")
(neg:SF (match_operand:SF 1 "register_operand" "")))]
""
"
{
rtx result;
rtx target;
 
if (TARGET_AM33_2)
{
emit_insn (gen_negsf2_am33_2 (operands[0], operands[1]));
DONE;
}
 
target = operand_subword_force (operands[0], 0, SFmode);
result = expand_binop (SImode, xor_optab,
operand_subword_force (operands[1], 0, SFmode),
GEN_INT (trunc_int_for_mode (0x80000000, SImode)),
target, 0, OPTAB_WIDEN);
gcc_assert (result);
 
if (result != target)
emit_move_insn (result, target);
 
/* Make a place for REG_EQUAL. */
emit_move_insn (operands[0], operands[0]);
DONE;
}")
 
(define_insn "negsf2_am33_2"
[(set (match_operand:SF 0 "register_operand" "=f,f")
(neg:SF (match_operand:SF 1 "register_operand" "0,?f")))]
"TARGET_AM33_2"
"@
fneg %0
fneg %1, %0"
[(set_attr "cc" "none_0hit")])
 
(define_expand "sqrtsf2"
[(set (match_operand:SF 0 "register_operand" "")
(sqrt:SF (match_operand:SF 1 "register_operand" "")))]
"TARGET_AM33_2 && flag_unsafe_math_optimizations"
"
{
rtx scratch = gen_reg_rtx (SFmode);
emit_insn (gen_rsqrtsf2 (scratch, operands[1], CONST1_RTX (SFmode)));
emit_insn (gen_divsf3 (operands[0], force_reg (SFmode, CONST1_RTX (SFmode)),
scratch));
DONE;
}")
 
(define_insn "rsqrtsf2"
[(set (match_operand:SF 0 "register_operand" "=f,f")
(div:SF (match_operand:SF 2 "const_1f_operand" "F,F")
(sqrt:SF (match_operand:SF 1 "register_operand" "0,?f"))))]
"TARGET_AM33_2"
"@
frsqrt %0
frsqrt %1, %0"
[(set_attr "cc" "none_0hit")])
 
(define_insn "addsf3"
[(set (match_operand:SF 0 "register_operand" "=f,f")
(plus:SF (match_operand:SF 1 "register_operand" "%0,f")
(match_operand:SF 2 "general_operand" "f,?fF")))]
"TARGET_AM33_2"
"@
fadd %2, %0
fadd %2, %1, %0"
[(set_attr "cc" "none_0hit")])
 
(define_insn "subsf3"
[(set (match_operand:SF 0 "register_operand" "=f,f")
(minus:SF (match_operand:SF 1 "register_operand" "0,f")
(match_operand:SF 2 "general_operand" "f,?fF")))]
"TARGET_AM33_2"
"@
fsub %2, %0
fsub %2, %1, %0"
[(set_attr "cc" "none_0hit")])
 
(define_insn "mulsf3"
[(set (match_operand:SF 0 "register_operand" "=f,f")
(mult:SF (match_operand:SF 1 "register_operand" "%0,f")
(match_operand:SF 2 "general_operand" "f,?fF")))]
"TARGET_AM33_2"
"@
fmul %2, %0
fmul %2, %1, %0"
[(set_attr "cc" "none_0hit")])
 
(define_insn "divsf3"
[(set (match_operand:SF 0 "register_operand" "=f,f")
(div:SF (match_operand:SF 1 "register_operand" "0,f")
(match_operand:SF 2 "general_operand" "f,?fF")))]
"TARGET_AM33_2"
"@
fdiv %2, %0
fdiv %2, %1, %0"
[(set_attr "cc" "none_0hit")])
 
(define_insn "fmaddsf4"
[(set (match_operand:SF 0 "register_operand" "=A")
(plus:SF (mult:SF (match_operand:SF 1 "register_operand" "%f")
(match_operand:SF 2 "register_operand" "f"))
(match_operand:SF 3 "register_operand" "f")))]
"TARGET_AM33_2"
"fmadd %1, %2, %3, %0"
[(set_attr "cc" "none_0hit")])
 
(define_insn "fmsubsf4"
[(set (match_operand:SF 0 "register_operand" "=A")
(minus:SF (mult:SF (match_operand:SF 1 "register_operand" "%f")
(match_operand:SF 2 "register_operand" "f"))
(match_operand:SF 3 "register_operand" "f")))]
"TARGET_AM33_2"
"fmsub %1, %2, %3, %0"
[(set_attr "cc" "none_0hit")])
 
(define_insn "fnmaddsf4"
[(set (match_operand:SF 0 "register_operand" "=A")
(minus:SF (match_operand:SF 3 "register_operand" "f")
(mult:SF (match_operand:SF 1 "register_operand" "%f")
(match_operand:SF 2 "register_operand" "f"))))]
"TARGET_AM33_2"
"fnmadd %1, %2, %3, %0"
[(set_attr "cc" "none_0hit")])
 
(define_insn "fnmsubsf4"
[(set (match_operand:SF 0 "register_operand" "=A")
(minus:SF (neg:SF (mult:SF (match_operand:SF 1 "register_operand" "%f")
(match_operand:SF 2 "register_operand" "f")))
(match_operand:SF 3 "register_operand" "f")))]
"TARGET_AM33_2"
"fnmsub %1, %2, %3, %0"
[(set_attr "cc" "none_0hit")])
 
 
;; ----------------------------------------------------------------------
;; PROLOGUE/EPILOGUE
;; ----------------------------------------------------------------------
(define_expand "prologue"
[(const_int 0)]
""
"expand_prologue (); DONE;")
 
(define_expand "epilogue"
[(return)]
""
"
{
expand_epilogue ();
DONE;
}")
 
(define_insn "return_internal"
[(const_int 2)
(return)]
""
"rets"
[(set_attr "cc" "clobber")])
 
;; This insn restores the callee saved registers and does a return, it
;; can also deallocate stack space.
(define_insn "return_internal_regs"
[(const_int 0)
(match_operand:SI 0 "const_int_operand" "i")
(return)]
""
"*
{
fputs (\"\\tret \", asm_out_file);
mn10300_print_reg_list (asm_out_file, mn10300_get_live_callee_saved_regs ());
fprintf (asm_out_file, \",%d\\n\", (int) INTVAL (operands[0]));
return \"\";
}"
[(set_attr "cc" "clobber")])
 
;; This instruction matches one generated by mn10300_gen_multiple_store()
(define_insn "store_movm"
[(match_parallel 0 "store_multiple_operation"
[(set (reg:SI 9) (plus:SI (reg:SI 9) (match_operand 1 "" "")))])]
""
"*
{
fputs (\"\\tmovm \", asm_out_file);
mn10300_print_reg_list (asm_out_file,
store_multiple_operation (operands[0], VOIDmode));
fprintf (asm_out_file, \",(sp)\\n\");
return \"\";
}"
[(set_attr "cc" "clobber")])
(define_insn "return"
[(return)]
"can_use_return_insn ()"
"*
{
rtx next = next_active_insn (insn);
 
if (next
&& GET_CODE (next) == JUMP_INSN
&& GET_CODE (PATTERN (next)) == RETURN)
return \"\";
else
return \"rets\";
}"
[(set_attr "cc" "clobber")])
 
;; Try to combine consecutive updates of the stack pointer (or any
;; other register for that matter).
(define_peephole
[(set (match_operand:SI 0 "register_operand" "=dxay")
(plus:SI (match_dup 0)
(match_operand 1 "const_int_operand" "")))
(set (match_dup 0)
(plus:SI (match_dup 0)
(match_operand 2 "const_int_operand" "")))]
""
"*
{
operands[1] = GEN_INT (INTVAL (operands[2]) + INTVAL (operands[1]));
return \"add %1,%0\";
}"
[(set_attr "cc" "clobber")])
 
;;
;; We had patterns to check eq/ne, but the they don't work because
;; 0x80000000 + 0x80000000 = 0x0 with a carry out.
;;
;; The Z flag and C flag would be set, and we have no way to
;; check for the Z flag set and C flag clear.
;;
;; This will work on the mn10200 because we can check the ZX flag
;; if the comparison is in HImode.
(define_peephole
[(set (cc0) (match_operand:SI 0 "register_operand" "dx"))
(set (pc) (if_then_else (ge (cc0) (const_int 0))
(match_operand 1 "" "")
(pc)))]
"dead_or_set_p (ins1, operands[0]) && REG_OK_FOR_INDEX_P (operands[0])"
"add %0,%0\;bcc %1"
[(set_attr "cc" "clobber")])
 
(define_peephole
[(set (cc0) (match_operand:SI 0 "register_operand" "dx"))
(set (pc) (if_then_else (lt (cc0) (const_int 0))
(match_operand 1 "" "")
(pc)))]
"dead_or_set_p (ins1, operands[0]) && REG_OK_FOR_INDEX_P (operands[0])"
"add %0,%0\;bcs %1"
[(set_attr "cc" "clobber")])
 
(define_peephole
[(set (cc0) (match_operand:SI 0 "register_operand" "dx"))
(set (pc) (if_then_else (ge (cc0) (const_int 0))
(pc)
(match_operand 1 "" "")))]
"dead_or_set_p (ins1, operands[0]) && REG_OK_FOR_INDEX_P (operands[0])"
"add %0,%0\;bcs %1"
[(set_attr "cc" "clobber")])
 
(define_peephole
[(set (cc0) (match_operand:SI 0 "register_operand" "dx"))
(set (pc) (if_then_else (lt (cc0) (const_int 0))
(pc)
(match_operand 1 "" "")))]
"dead_or_set_p (ins1, operands[0]) && REG_OK_FOR_INDEX_P (operands[0])"
"add %0,%0\;bcc %1"
[(set_attr "cc" "clobber")])
 
(define_expand "int_label"
[(unspec [(match_operand:SI 0 "" "")] UNSPEC_INT_LABEL)]
"" "")
 
(define_expand "GOTaddr2picreg"
[(match_dup 0)]
"" "
{
/* It would be nice to be able to have int_label keep track of the
counter and all, but if we add C code to it, we'll get an insn
back, and we just want the pattern. */
operands[0] = gen_int_label (GEN_INT (mn10300_unspec_int_label_counter++));
if (TARGET_AM33)
emit_insn (gen_am33_loadPC (operands[0]));
else
emit_insn (gen_mn10300_loadPC (operands[0]));
emit_insn (gen_add_GOT_to_pic_reg (operands[0]));
DONE;
}
")
 
(define_insn "am33_loadPC"
[(parallel
[(set (reg:SI PIC_REG) (pc))
(use (match_operand 0 "" ""))])]
"TARGET_AM33"
"%0:\;mov pc,a2")
 
 
(define_insn_and_split "mn10300_loadPC"
[(parallel
[(set (reg:SI PIC_REG) (pc))
(use (match_operand 0 "" ""))])]
""
"#"
"reload_completed"
[(match_operand 0 "" "")]
"
{
rtx sp_reg = gen_rtx_REG (SImode, SP_REG);
int need_stack_space = (get_frame_size () == 0
&& current_function_outgoing_args_size == 0);
 
if (need_stack_space)
emit_move_insn (sp_reg, plus_constant (sp_reg, -4));
 
emit_insn (gen_call_next_insn (operands[0]));
 
if (need_stack_space)
emit_insn (gen_pop_pic_reg ());
else
emit_move_insn (pic_offset_table_rtx, gen_rtx_MEM (SImode, sp_reg));
 
DONE;
}")
 
(define_insn "call_next_insn"
[(parallel
[(set (mem:SI (reg:SI SP_REG)) (pc))
(use (match_operand 0 "" ""))])]
"reload_completed"
"calls %0\;%0:")
 
(define_expand "add_GOT_to_pic_reg"
[(set (reg:SI PIC_REG)
(plus:SI
(reg:SI PIC_REG)
(const
(unspec [(minus:SI
(match_dup 1)
(const (minus:SI
(const (match_operand:SI 0 "" ""))
(pc))))
] UNSPEC_PIC))))]
""
"
{
operands[1] = gen_rtx_SYMBOL_REF (VOIDmode, GOT_SYMBOL_NAME);
}")
 
(define_expand "symGOT2reg"
[(match_operand:SI 0 "" "")
(match_operand:SI 1 "" "")]
""
"
{
rtx insn = emit_insn (gen_symGOT2reg_i (operands[0], operands[1]));
 
MEM_READONLY_P (SET_SRC (PATTERN (insn))) = 1;
 
REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_EQUAL, operands[1],
REG_NOTES (insn));
 
DONE;
}")
 
(define_expand "symGOT2reg_i"
[(set (match_operand:SI 0 "" "")
(mem:SI (plus:SI (reg:SI PIC_REG)
(const (unspec [(match_operand:SI 1 "" "")]
UNSPEC_GOT)))))]
""
"")
 
(define_expand "symGOTOFF2reg"
[(match_operand:SI 0 "" "") (match_operand:SI 1 "" "")]
""
"
{
rtx insn = emit_insn (gen_symGOTOFF2reg_i (operands[0], operands[1]));
 
REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_EQUAL, operands[1],
REG_NOTES (insn));
 
DONE;
}")
 
(define_expand "symGOTOFF2reg_i"
[(set (match_operand:SI 0 "" "")
(const (unspec [(match_operand:SI 1 "" "")] UNSPEC_GOTOFF)))
(set (match_dup 0) (plus:SI (match_dup 0) (reg:SI PIC_REG)))]
""
"")
 
(define_expand "sym2PIC"
[(unspec [(match_operand:SI 0 "" "")] UNSPEC_PIC)]
"" "")
 
(define_expand "sym2PLT"
[(unspec [(match_operand:SI 0 "" "")] UNSPEC_PLT)]
"" "")

powered by: WebSVN 2.1.0

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