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)] |
"" "") |