aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSteve Chamberlain <sac@gnu.org>1994-02-05 21:03:19 +0000
committerSteve Chamberlain <sac@gnu.org>1994-02-05 21:03:19 +0000
commit0d7e008e87f916fe7ff510696ec4fabbcad43925 (patch)
tree8f2f22c60ed2077b0d103bbb3ee10a5de7ef8ee4
parent2c65021a94a2a2020bd2c76d147a747c6f4e5928 (diff)
downloadgcc-0d7e008e87f916fe7ff510696ec4fabbcad43925.zip
gcc-0d7e008e87f916fe7ff510696ec4fabbcad43925.tar.gz
gcc-0d7e008e87f916fe7ff510696ec4fabbcad43925.tar.bz2
*** empty log message ***
From-SVN: r6486
-rw-r--r--gcc/config/sh/sh.c2228
-rw-r--r--gcc/config/sh/sh.h494
-rw-r--r--gcc/config/sh/sh.md1535
3 files changed, 2806 insertions, 1451 deletions
diff --git a/gcc/config/sh/sh.c b/gcc/config/sh/sh.c
index ca8f8f0..d8d73ba 100644
--- a/gcc/config/sh/sh.c
+++ b/gcc/config/sh/sh.c
@@ -1,21 +1,21 @@
/* Output routines for GCC for Hitachi Super-H
- Copyright (C) 1993 Free Software Foundation, Inc.
+ Copyright (C) 1993, 1994 Free Software Foundation, Inc.
-This file is part of GNU CC.
+ This file is part of GNU CC.
-GNU CC is free software; you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation; either version 2, or (at your option)
-any later version.
+ GNU CC is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
-GNU CC 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.
+ GNU CC 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 GNU CC; see the file COPYING. If not, write to
-the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+ You should have received a copy of the GNU General Public License
+ along with GNU CC; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
/* Contributed by Steve Chamberlain (sac@cygnus.com) */
@@ -32,27 +32,30 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
#include "insn-flags.h"
#include "tree.h"
#include "output.h"
+
#include "insn-attr.h"
#include "flags.h"
#include "obstack.h"
#include "expr.h"
+static rtx add_constant ();
-static int add_constant ();
-int dump_constants ();
+int pragma_interrupt;
+int pragma_trapa;
int current_function_anonymous_args;
extern int current_function_pretend_args_size;
extern char *version_string;
extern int flag_traditional;
-
+static rtx shiftsyms[32];
+struct rtx_def *table_lab;
enum attr_cpu sh_cpu; /* target cpu */
/* Global variables for machine-dependent things. */
/* Saved operands from the last compare to use when we generate an scc
- or bcc insn. */
+ or bcc insn. */
rtx sh_compare_op0;
rtx sh_compare_op1;
@@ -62,12 +65,12 @@ rtx sh_compare_op1;
int regno_reg_class[FIRST_PSEUDO_REGISTER] =
{
- R0_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS,
+ R0_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS,
GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS,
GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS,
GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS,
- GENERAL_REGS, PR_REGS, T_REGS, NO_REGS, MAC_REGS,
- MAC_REGS,
+ GENERAL_REGS, PR_REGS, T_REGS, NO_REGS,
+ MAC_REGS, MAC_REGS,
};
/* Provide reg_class from a letter such as appears in the machine
@@ -84,24 +87,45 @@ enum reg_class reg_class_from_letter[] =
/* y */ NO_REGS, /* z */ R0_REGS
};
+/* Value is 1 if register/mode pair is acceptable on SH. Even
+ registers can hold DIs and DF values. The rest can only hold
+ SI's efficiently */
+
+
+#define REG_ODD \
+ ( (1 << (int) QImode) | (1 << (int) HImode) | (1 << (int) SImode) \
+ | (1 << (int) QFmode) | (1 << (int) HFmode) | (1 << (int) SFmode) \
+ | (1 << (int) CQImode) | (1 << (int) CHImode))
+#define REG_EVEN \
+ (REG_ODD | (1 << (int) DImode) | (1 << (int) DFmode) \
+ | (1 << (int) CSImode) | (1 << (int) SCmode))
+#define SI_ONLY (1<<(int)SImode)
+int hard_regno_mode_ok[] =
+{
+ REG_EVEN, REG_ODD, REG_EVEN, REG_ODD,
+ REG_EVEN, REG_ODD, REG_EVEN, REG_ODD,
+ REG_EVEN, REG_ODD, REG_EVEN, REG_ODD,
+ REG_EVEN, REG_ODD, REG_EVEN, REG_ODD,
+ REG, 0, SI_ONLY, SI_ONLY,
+ SI_ONLY, SI_ONLY
+};
/* Local label counter, used for constants in the pool and inside
pattern branches. */
-
static int lf = 100;
-/* Used to work out sizes of instructions */
-static int first_pc;
-static int pc;
-#define MAYBE_DUMP_LEVEL 900
-#define MUST_DUMP_LEVEL 1000
-static int dumpnext;
+
+/* Number of bytes pushed for anonymous args, used to pass information
+ between expand_prologue and expand_epilogue. */
+static int extra_push;
+
void
push (rn)
+ int rn;
{
emit_insn (gen_push (gen_rtx (REG, SImode, rn)));
}
@@ -116,8 +140,7 @@ pop (rn)
/* Adjust the stack and return the number of bytes taken to do it */
static void
-output_stack_adjust (direction, size)
- int direction;
+output_stack_adjust (size)
int size;
{
if (size)
@@ -125,24 +148,18 @@ output_stack_adjust (direction, size)
rtx val = GEN_INT (size);
rtx insn;
- if (size > 120)
+ if (!CONST_OK_FOR_I (size))
{
- rtx nval = gen_rtx (REG, SImode, 13);
+ rtx nval = gen_rtx (REG, SImode, 3);
emit_insn (gen_movsi (nval, val));
val = nval;
}
- if (direction > 0)
- insn = gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx, val);
- else
- insn = gen_subsi3 (stack_pointer_rtx, stack_pointer_rtx, val);
-
+ insn = gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx, val);
emit_insn (insn);
}
}
-
-
/* Generate code to push the regs specified in the mask, and return
the number of bytes the insns take. */
@@ -151,7 +168,6 @@ push_regs (mask)
int mask;
{
int i;
- int size = 0;
for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
{
@@ -163,13 +179,11 @@ push_regs (mask)
}
-/*
- Print an instruction which would have gone into a delay slot
- after an instructiuon, but couldn't because the instruction expanded
- into a sequence where putting the slot insn at the end wouldn't work.
- */
+/* Print an instruction which would have gone into a delay slot after
+ an instructiuon, but couldn't because the instruction expanded into a
+ sequence where putting the slot insn at the end wouldn't work. */
-void
+static void
print_slot (insn)
rtx insn;
{
@@ -178,35 +192,65 @@ print_slot (insn)
INSN_DELETED_P (XVECEXP (insn, 0, 1)) = 1;
}
-/* Number of bytes pushed for anonymous args */
-
-static int extra_push;
/* Work out the registers which need to be saved, both as a mask and a
- count */
+ count.
-int
-calc_live_regs (count)
- int *count;
+ If doing a pragma interrupt function, then push all regs used by the function,
+ and if we call another function (we can tell by looking at PR), make sure that all the
+ regs it clobbers are safe too.
+ */
+static int
+calc_live_regs (count_ptr)
+ int *count_ptr;
{
int reg;
int live_regs_mask = 0;
- *count = 0;
-
+ int count = 0;
for (reg = 0; reg < FIRST_PSEUDO_REGISTER; reg++)
{
- if (regs_ever_live[reg] && !call_used_regs[reg])
+ if (reg == ARG_POINTER_REGNUM)
+ continue;
+ if (reg == T_REG)
+ continue;
+ if (reg == GBR_REG)
+ continue;
+
+ if (pragma_interrupt && !pragma_trapa)
{
- (*count)++;
- live_regs_mask |= (1 << reg);
+ /* Need to save all the regs ever live */
+ if ((regs_ever_live[reg]
+ || (call_used_regs[reg] && regs_ever_live[PR_REG]))
+ && reg != 15)
+ {
+ live_regs_mask |= 1 << reg;
+ count++;
+ }
+ }
+ else if (TARGET_SMALLCALL)
+ {
+ /* Don't need to push anthing, but count the regs which have
+ been pushed by the wrapper */
+ if (call_used_regs[reg])
+ count++;
+ }
+ else
+ {
+ /* Only push those regs which are used and need to be saved */
+ if (regs_ever_live[reg] && !call_used_regs[reg])
+ {
+ count++;
+ live_regs_mask |= (1 << reg);
+ }
}
}
+
+
+ *count_ptr = count;
return live_regs_mask;
}
-
-
static int
need_slot (insn)
rtx insn;
@@ -287,7 +331,7 @@ print_operand_address (stream, x)
a double word value
'O' print a constant without the #
'M' print a constant as its negative
- 'I' put something into the constant pool and print its label */
+ 'N' print insides of a @++ or @-- o */
void
print_operand (stream, x, code)
@@ -297,8 +341,6 @@ print_operand (stream, x, code)
{
switch (code)
{
-
-
case '.':
if (need_slot (final_sequence))
fprintf (stream, ".s");
@@ -306,13 +348,9 @@ print_operand (stream, x, code)
case '*':
fprintf (stream, "LF%d", lf);
break;
- case '!':
- dump_constants (0);
- break;
case '^':
lf++;
break;
-
case '#':
/* Output a nop if there's nothing in the delay slot */
if (dbr_sequence_length () == 0)
@@ -321,31 +359,26 @@ print_operand (stream, x, code)
}
break;
case 'O':
- fprintf (asm_out_file, "%d", INTVAL (x));
- break;
-
- case 'I':
- fprintf (asm_out_file, "LK%d", add_constant (x, SImode));
+ output_addr_const (stream, x);
break;
-
case 'M':
fprintf (asm_out_file, "#%d", -INTVAL (x));
break;
-
+ case 'N':
+ fputs (reg_names[REGNO (XEXP (XEXP (x, 0), 0))], (stream));
+ break;
case 'R':
- /* Next location along in memory or register*/
+ /* Next location along in memory or register */
switch (GET_CODE (x))
{
case REG:
fputs (reg_names[REGNO (x) + 1], (stream));
break;
case MEM:
- print_operand_address (stream,
- XEXP (adj_offsettable_operand (x, 4), 0), 0);
+ print_operand_address (stream, XEXP (adj_offsettable_operand (x, 4), 0));
break;
}
break;
-
default:
switch (GET_CODE (x))
{
@@ -359,43 +392,241 @@ print_operand (stream, x, code)
fputc ('#', stream);
output_addr_const (stream, x);
break;
-
}
break;
}
}
+sextb (x)
+{
+ x &= 0xff;
+ if (x > 127)
+ {
+ x = -256 + x;
+ }
+ return x;
+}
-/* Define the offset between two registers, one to be eliminated, and
- the other its replacement, at the start of a routine. */
-int
-initial_elimination_offset (from, to)
+
+/* Take a move with integer constant source in OPERANDS, see if it can be generated by
+ devious shifting. If so, generate the instruction sequence and return 1, otherwise
+ return 0.
+
+ OPERANDS[0] Destination register
+ OPERANDS[1] Source constant
+
+ 00000000 00000000 00000000 0NNNNNNNN simple load
+ 00000000 00000000 00000000 NNNNNNNN0 load and shift by 1
+ 00000000 00000000 0000000N NNNNNNN00 load and shift by 2
+ 00000000 00000000 0NNNNNNN 000000000 load and shift by 8
+ 00000000 0NNNNNNN 00000000 000000000 load and shift by 16
+ N0000000 00000000 00000000 00NNNNNNN load and rotate right
+
+ 11111111 11111111 11111111 1NNNNNNNN simple load
+ 11111111 11111111 11111111 NNNNNNNN0 load and shift by 1
+ 11111111 11111111 1111111N NNNNNNN00 load and shift by 2
+ 11111111 11111111 1NNNNNNN 000000000 load and shift by 8
+ 11111111 1NNNNNNN 00000000 000000000 load and shift by 16
+ N1111111 11111111 11111111 11NNNNNNN load and rotate right
+
+ 00000000 00000000 00000000 1NNNNNNNN load and zero extend byte
+ 00000000 00000000 11111111 1NNNNNNNN load and zero extend word
+
+
+*/
+
+static int
+synth_constant (operands, mode)
+ rtx operands[];
+ enum machine_mode mode;
{
- int regs_saved;
- int d = calc_live_regs (&regs_saved);
- int total_saved_regs_space = (regs_saved) * 4;
- int total_auto_space = get_frame_size ();
+ rtx dst;
+ int i = INTVAL (operands[1]) & 0xffffffff;
+
+ if (CONST_OK_FOR_I (i))
+ return 0;
+ dst = mode == SImode ? operands[0] : gen_reg_rtx (SImode);
- if (from == ARG_POINTER_REGNUM && to == FRAME_POINTER_REGNUM)
+ /* 00000000 00000000 11111111 1NNNNNNNN load and zero extend word */
+ if ((i & 0xffffff80) == 0x0000ff80)
{
- return total_saved_regs_space;
+ emit_move_insn (dst, GEN_INT (sextb (i)));
+ emit_insn (gen_and_ffff (dst, dst));
}
+ /* 00000000 00000000 00000000 1NNNNNNNN load and zero extend byte */
+ else if ((i & 0xffffff80) == 0x00000080)
+ {
+ emit_move_insn (dst, GEN_INT (sextb (i)));
+ emit_insn (gen_and_ff (dst, dst));
+ }
+ /* 00000000 00000000 00000000 NNNNNNNN0 load and shift by 1
+ 11111111 11111111 11111111 NNNNNNNN0 load and shift by 1 */
+ else if ((i & 0xffffff01) == 0
+ || (i & 0xffffff01) == 0xffffff00)
+ {
+ emit_move_insn (dst, GEN_INT (sextb (i >> 1)));
+ emit_insn (gen_ashlsi3_n (dst, dst, GEN_INT (1)));
+ }
+ /* 00000000 00000000 0000000N NNNNNNN00 load and shift by 2
+ 11111111 11111111 1111111N NNNNNNN00 load and shift by 2*/
+ else if ((i & 0xfffffe03) == 0
+ || (i & 0xfffffe03) == 0xfffffe00)
+ {
+ emit_move_insn (dst, GEN_INT (sextb (i >> 2)));
+ emit_insn (gen_ashlsi3_n (dst, dst, GEN_INT (2)));
+ }
+ /* 00000000 00000000 0NNNNNNN 000000000 load and shift by 8
+ 11111111 11111111 1NNNNNNN 000000000 load and shift by 8 */
- if (from == ARG_POINTER_REGNUM && to == STACK_POINTER_REGNUM)
+ else if ((i & 0xffff80ff) == 0
+ || (i & 0xffff80ff) == 0xffff8000)
{
- return total_saved_regs_space + total_auto_space;
+ emit_move_insn (dst, GEN_INT (sextb (i >> 8)));
+ emit_insn (gen_ashlsi3_n (dst, dst, GEN_INT (8)));
}
+ /* 00000000 0NNNNNNN 00000000 000000000 load and shift by 16
+ 11111111 1NNNNNNN 00000000 000000000 load and shift by 16 */
+ else if ((i & 0xff80ffff) == 0
+ || (i & 0xff80ffff) == 0xff80ffff)
+ {
+ emit_move_insn (dst, GEN_INT (sextb (i >> 16)));
+ emit_insn (gen_ashlsi3_n (dst, dst, GEN_INT (16)));
+ }
+ /* 00000000 00000000 0NNNNNNN 0NNNNNNNN load shift 8 and add */
+ else if ((i & 0xffff8080) == 0 && TARGET_CLEN3)
+ {
+ emit_move_insn (dst, GEN_INT (sextb (i >> 8)));
+ emit_insn (gen_ashlsi3_n (dst, dst, GEN_INT (8)));
+ emit_insn (gen_addsi3 (dst, dst, GEN_INT (i & 0x7f)));
+ }
+ else
+ return 0;
- if (from == FRAME_POINTER_REGNUM && to == STACK_POINTER_REGNUM)
+ if (mode != SImode)
{
- return total_auto_space;
+ emit_insn (gen_rtx (SET, VOIDmode, operands[0],
+ gen_rtx (SUBREG, mode, dst, 0)));
+
}
+ return 1;
}
+/* Emit code to perform a block move. Choose the best method.
+
+ OPERANDS[0] is the destination.
+ OPERANDS[1] is the source.
+ OPERANDS[2] is the size.
+ OPERANDS[3] is the alignment safe to use. */
+
+
+int
+expand_block_move (operands)
+ rtx *operands;
+{
+ int align = INTVAL (operands[3]);
+ int constp = (GET_CODE (operands[2]) == CONST_INT);
+ int bytes = (constp ? INTVAL (operands[2]) : 0);
+ enum machine_mode mode;
+ /* IF odd then fail */
+ if (!constp || bytes <= 0)
+ return 0;
+
+ switch (align)
+ {
+ case 1:
+ mode = QImode;
+ break;
+ case 2:
+ mode = HImode;
+ break;
+ default:
+ mode = SImode;
+ align = 4;
+ }
+ if (mode == SImode && constp && bytes < 64 && (bytes % 4 == 0))
+ {
+ char entry[30];
+ tree entry_name;
+ rtx func_addr_rtx;
+ rtx r4 = gen_rtx (REG, SImode, 4);
+ rtx r5 = gen_rtx (REG, SImode, 5);
+ sprintf (entry, "__movstr%s%d", GET_MODE_NAME (mode), bytes);
+ entry_name = get_identifier (entry);
+
+ func_addr_rtx = copy_to_mode_reg (Pmode,
+ gen_rtx (SYMBOL_REF, Pmode, IDENTIFIER_POINTER (entry_name)));
+ emit_insn (gen_move_insn (r4, XEXP (operands[0], 0)));
+ emit_insn (gen_move_insn (r5, XEXP (operands[1], 0)));
+ emit_insn (gen_block_move_real (func_addr_rtx));
+ return 1;
+ }
+ if (mode == SImode && constp && (bytes % 4 == 0))
+ {
+ char entry[30];
+ tree entry_name;
+ rtx func_addr_rtx;
+ int groups;
+ rtx r4 = gen_rtx (REG, SImode, 4);
+ rtx r5 = gen_rtx (REG, SImode, 5);
+ rtx r6 = gen_rtx (REG, SImode, 6);
+ entry_name = get_identifier ("__movstr");
+
+ func_addr_rtx = copy_to_mode_reg (Pmode,
+ gen_rtx (SYMBOL_REF, Pmode,
+ IDENTIFIER_POINTER (entry_name)));
+ emit_insn (gen_move_insn (r4, XEXP (operands[0], 0)));
+ emit_insn (gen_move_insn (r5, XEXP (operands[1], 0)));
+
+ /* r6 controls the size of the move, 16 is decremented from it
+ for each 64 bytes moved, then the -ve bit is used as an index into a
+ list of move instructions like this:
+
+ {
+ do {
+ *dst++ = *src++;
+ *dst++ = *src++;
+ *dst++ = *src++;
+ ..etc.. 16 in all
+ *dst++ = *src++;
+ *dst++ = *src++;
+ size -= 16;
+ } while (size > 0);
+
+ switch (size)
+ {
+ case -15:
+ *dst++ = *src++;
+ case -14:
+ *dst++ = *src++;
+ .. etc.. ;
+ case -2:
+ *dst++ = *src++;
+ case -1:
+ *dst++ = *src++;
+ case 0:
+ ;
+ }
+ }
+
+ eg, a 72 byte move would be set up with size(r6) = 14, for one
+ iteration through the big while loop, and a switch of -2 for the last part */
+
+ {
+ int final_switch = 16 - ((bytes / 4) % 16);
+ int while_loop = ((bytes / 4) / 16 - 1) * 16;
+ emit_insn (gen_move_insn (r6, GEN_INT (while_loop + final_switch)));
+ emit_insn (gen_block_lump_real (func_addr_rtx));
+ return 1;
+ }
+ }
+ return 0;
+
+}
+
/* Prepare operands for a move define_expand; specifically, one of the
operands must be in a register. Take this chance to remove
addressing modes which can't be coped with very well. */
@@ -405,38 +636,130 @@ prepare_move_operands (operands, mode)
rtx operands[];
enum machine_mode mode;
{
- /* One of the operands has to be a register */
- if ((!register_operand (operands[0], mode)
- && !register_operand (operands[1], mode))
- || GET_CODE (operands[1]) == PLUS)
+ if (!(reload_in_progress || reload_completed)
+ && ((!register_operand (operands[0], mode)
+ && !register_operand (operands[1], mode))
+ || GET_CODE (operands[1]) == PLUS))
{
/* copy the source to a register */
operands[1] = copy_to_mode_reg (mode, operands[1]);
}
-
- /* If we've got a negative index, break it down */
-
- if (GET_CODE (operands[0]) == MEM && !reload_in_progress)
+ if ((mode == DImode || mode == SImode || mode == HImode || mode == QImode)
+ && GET_CODE (operands[1]) == CONST_INT)
+ {
+ return synth_constant (operands, mode);
+ }
+ if (mode == DFmode || mode == DImode)
{
+ rtx src = operands[1];
+ rtx dst = operands[0];
+ rtx insns;
- rtx inside = XEXP (operands[0], 0);
- if (GET_CODE (inside) == PLUS)
+ if (src == dst)
{
- rtx inside1 = XEXP (inside, 1);
- if (GET_CODE (inside1) == CONST_INT
- && INTVAL (inside1) < 0)
- {
- /* Catch this now and break it into bits, it will only cause
- problems later */
+ emit_insn (gen_rtx (SET, VOIDmode, dst, src));
+ return 1;
+ }
- rtx sub = copy_to_mode_reg (SImode, inside);
- XEXP (operands[0], 0) = sub;
- }
+ if (GET_CODE (src) == REG &&
+ REGNO (src) >= FIRST_PSEUDO_REGISTER)
+ return 0;
+
+ if (GET_CODE (dst) == REG &&
+ REGNO (dst) >= FIRST_PSEUDO_REGISTER)
+ return 0;
+
+
+ if (push_operand (dst, mode))
+ return 0;
+
+ if (GET_CODE (src) == CONST_DOUBLE)
+ src = force_const_mem (DFmode, src);
+
+ if (reload_in_progress)
+ {
+ if (!(offsettable_memref_p (src) || register_operand (src, mode)))
+ return 0;
+ if (!(offsettable_memref_p (dst) || register_operand (dst,
+ mode)))
+ return 0;
}
+ start_sequence ();
+ if (GET_CODE (operands[0]) != REG
+ || !refers_to_regno_p (REGNO (operands[0]), REGNO (operands[0]) + 1, operands[1], 0))
+ {
+ emit_move_insn (operand_subword (dst, 0, 1, mode),
+ operand_subword_force (src, 0, mode));
+ emit_move_insn (operand_subword (dst, 1, 1, mode),
+ operand_subword_force (src, 1, mode));
+ }
+ else
+ {
+ emit_move_insn (operand_subword (dst, 1, 1, mode),
+ operand_subword_force (src, 1, mode));
+ emit_move_insn (operand_subword (dst, 0, 1, mode),
+ operand_subword_force (src, 0, mode));
+ }
+
+ insns = get_insns ();
+ end_sequence ();
+
+ emit_no_conflict_block (insns, dst, src, 0, src);
+ return 1;
}
+
return 0;
}
+/* Work out the subword parts to split up a double move
+ into two SI moves - take care to do it in the right order
+ */
+
+int
+prepare_split_double_ops (operands, mode)
+ rtx operands[];
+ enum machine_mode mode;
+{
+ if (GET_CODE (operands[1]) == REG
+ && REGNO (operands[1]) > FIRST_PSEUDO_REGISTER)
+ return 0;
+
+ if (GET_CODE (operands[0]) == REG
+ && REGNO (operands[0]) > FIRST_PSEUDO_REGISTER)
+ return 0;
+
+ /* If we split move insns from memory, it confuses scheduling
+ later on. */
+ if (GET_CODE (operands[1]) == MEM)
+ return 0;
+ if (GET_CODE (operands[0]) == MEM)
+ return 0;
+
+ if (GET_CODE (operands[0]) != REG
+ || !refers_to_regno_p (REGNO (operands[0]),
+ REGNO (operands[0]) + 1, operands[1], 0))
+ {
+ operands[2] = operand_subword (operands[0], 0, 0, mode);
+ operands[3] = operand_subword (operands[1], 0, 0, mode);
+ operands[4] = operand_subword (operands[0], 1, 0, mode);
+ operands[5] = operand_subword (operands[1], 1, 0, mode);
+ }
+ else
+ {
+ operands[2] = operand_subword (operands[0], 1, 0, mode);
+ operands[3] = operand_subword (operands[1], 1, 0, mode);
+ operands[4] = operand_subword (operands[0], 0, 0, mode);
+ operands[5] = operand_subword (operands[1], 0, 0, mode);
+ }
+
+ if (operands[2] == 0 || operands[3] == 0
+ || operands[4] == 0 || operands[5] == 0)
+ return 0;
+
+ emit_move_insn (operands[2], operands[3]);
+ emit_move_insn (operands[4], operands[5]);
+ return 1;
+}
/* Prepare the operands for an scc instruction; make sure that the
compare has been done. */
@@ -446,17 +769,44 @@ prepare_scc_operands (code)
if (GET_CODE (sh_compare_op0) != REG
|| REGNO (sh_compare_op0) != T_REG)
{
+ int newcode = code;
/* First need a compare insn */
- emit_insn (gen_rtx (SET, SImode,
+ switch (code)
+ {
+ case NE:
+ newcode = EQ;
+ break;
+ case LT:
+ newcode = GT;
+ break;
+ case LE:
+ newcode = GE;
+ break;
+ case LTU:
+ newcode = GTU;
+ break;
+ case LEU:
+ newcode = GEU;
+ break;
+ }
+ if (newcode != code)
+ {
+ rtx tmp = sh_compare_op0;
+ sh_compare_op0 = sh_compare_op1;
+ sh_compare_op1 = tmp;
+ code = newcode;
+ }
+
+ sh_compare_op0 = force_reg (SImode, sh_compare_op0);
+ emit_insn (gen_rtx (SET, VOIDmode,
gen_rtx (REG, SImode, T_REG),
- gen_rtx (code, SImode, sh_compare_op0,
- sh_compare_op1)));
+ gen_rtx (code, SImode, sh_compare_op0, sh_compare_op1)));
}
return gen_rtx (REG, SImode, T_REG);
}
-/* Functions to output assembly */
+/* Functions to output assembly code. */
/* Return a sequence of instructions to perform DI or DF move.
@@ -464,15 +814,24 @@ prepare_scc_operands (code)
to take care when we see overlapping source and dest registers.
*/
+
char *
-output_movedouble (operands, mode)
+output_movedouble (insn, operands, mode)
+ rtx insn;
rtx operands[];
enum machine_mode mode;
{
rtx dst = operands[0];
rtx src = operands[1];
- int lowfirst;
+ fprintf (asm_out_file, "! move double \n");
+ fprintf (asm_out_file, "! pc %04x\n", insn_addresses[INSN_UID (insn)]);
+ if (GET_CODE (dst) == MEM
+ && GET_CODE (XEXP (dst, 0)) == POST_INC)
+ {
+ operands[0] = XEXP (XEXP (dst, 0), 0);
+ return "mov.l %R1,@(4,%0)\n\tmov.l %1,@%0\n\tadd #8,%0";
+ }
if (register_operand (dst, mode)
&& register_operand (src, mode))
{
@@ -480,24 +839,31 @@ output_movedouble (operands, mode)
return "sts mach,%0\n\tsts macl,%R0";
/*
- when mov.d r1,r2 do r2->r3 then r1->r2
- when mov.d r1,r0 do r1->r0 then r2->r1
- */
+ when mov.d r1,r2 do r2->r3 then r1->r2
+ when mov.d r1,r0 do r1->r0 then r2->r1
+ */
if (REGNO (src) + 1 == REGNO (dst))
- return "mov %1,%0\n\tmov %R1,%R0 ! cr";
+ return "mov %R1,%R0\n\tmov %1,%0 ! cra";
else
- return "mov %R1,%R0\n\tmov %1,%0 ";
-
+ return "mov %1,%0\n\tmov %R1,%R0 ! crb";
}
else if (GET_CODE (src) == CONST_INT)
{
- if (INTVAL (src) < 0)
- return "mov #-1,%0\n\tmov %1,%R0";
+ HOST_WIDE_INT val = INTVAL (src);
+ int rn = REGNO (operands[0]);
+ if (val < 0)
+ {
+ fprintf (asm_out_file, "\tmov #-1,r%d\n", rn);
+ }
else
- return "mov #0,%0\n\tmov %1,%R0";
- }
+ {
+ fprintf (asm_out_file, "\tmov #0,r%d\n", rn);
+ }
+ fprintf (asm_out_file, "\tmov #%d,r%d\n", val, rn + 1);
+ return "";
+ }
else if (GET_CODE (src) == MEM)
{
int ptrreg1 = -1;
@@ -518,10 +884,13 @@ output_movedouble (operands, mode)
if (GET_CODE (rhs) == REG)
ptrreg2 = REGNO (rhs);
}
+ else if (GET_CODE (inside) == LABEL_REF)
+ {
+ return "mov.l %1,%0\n\tmov.l %1+4,%R0";
+ }
else
abort ();
-
if ((ptrreg1 >= 0 && ptrreg2 >= 0)
&& (dreg == ptrreg1
|| dreg == ptrreg2
@@ -582,7 +951,24 @@ output_shift (string, reg, k, code)
{
int s = INTVAL (k);
-
+ if (s < 0)
+ {
+ s = -s;
+ switch (code)
+ {
+ case LSHIFTRT:
+ case ASHIFTRT:
+ code = LSHIFT;
+ break;
+ case ASHIFT:
+ code = ASHIFTRT;
+ break;
+ case LSHIFT:
+ code = LSHIFTRT;
+ default:
+ abort ();
+ }
+ }
if (code == ASHIFT && s == 31)
{
/* Shift left by 31 moving into the t bit, clearing and rotating the other way */
@@ -632,13 +1018,80 @@ output_shift (string, reg, k, code)
return "";
}
+
+void
+function_epilogue (stream, size)
+ FILE *stream;
+ int size;
+{
+ fprintf (stream, "\trts\n");
+ fprintf (stream, "\tor r0,r0\n");
+}
+
+
/* Return the text of the branch instruction which matches its length
attribute.
This gets tricky if we have an insn in the delay slot of a branch
- and the branch needs more than 1 insn to complete.*/
+ and the branch needs more than 1 insn to complete. */
+int pending_const_table;
+ /* We can't tell if we need a register as a scratch for the jump
+ until after branch shortening, and then it's too late to allocate a
+ register the 'proper' way. These instruction sequences are rare
+ anyway, so to avoid always using a reg up from our limited set, we'll
+ grab one when we need one on output. */
+
+char *
+output_far_jump (insn, op)
+ rtx insn;
+ rtx op;
+{
+ rtx thislab = gen_label_rtx ();
+
+ /* See if we can grab a reg from the prev insn */
+ rtx gotone = 0;
+ rtx prev = PREV_INSN (insn);
+ rtx link;
+
+ if (dbr_sequence_length ())
+ {
+ /* Something to go in what would have been the delay
+ slot if this had been a short branch. Make sure the
+ reg we use to generate the branch target address
+ doesn't conflict */
+
+ int i;
+ rtx vec[2];
+ vec[0] = thislab;
+
+ for (i = 0; i < 8; i++)
+ {
+ vec[1] = gen_rtx (REG, SImode, i);
+ if (!reg_referenced_p (vec[1], PATTERN (XVECEXP (final_sequence, 0, 1))))
+ break;
+ }
+
+ output_asm_insn ("mov.l %1,@-r15", vec);
+ output_asm_insn ("mov.l %O0,%1", vec);
+ print_slot (final_sequence);
+ output_asm_insn ("jmp @%1 ! 32 xcond", vec);
+ output_asm_insn ("mov.l @r15+,%1", vec);
+ }
+ else
+ {
+ output_asm_insn ("mov.l r13,@-r15", 0);
+ output_asm_insn ("mov.l %O0,r13", &thislab);
+ output_asm_insn ("jmp @r13 ! 32 zcond", 0);
+ output_asm_insn ("mov.l @r15+,r13", 0);
+ }
+
+ ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "L", CODE_LABEL_NUMBER (thislab));
+ output_asm_insn (".align 2", 0);
+ output_asm_insn (".long %O0", &op);
+ return "";
+}
char *
output_branch (logic, insn)
@@ -649,6 +1102,7 @@ output_branch (logic, insn)
int label = lf++;
int rn = -1;
int need_save;
+ fprintf (asm_out_file, "! pc %04x\n", insn_addresses[INSN_UID (insn)]);
switch (get_attr_length (insn))
{
@@ -679,16 +1133,13 @@ output_branch (logic, insn)
output_asm_insn ("bra %l0 ! 12 bit cond ", recog_operand);
fprintf (asm_out_file, "\tor r0,r0\n");
- label = dump_constants (label);
fprintf (asm_out_file, "LF%d:\n", label);
}
-
return "";
- case 8:
+ case 16:
/* Branches a long way away */
{
-
rtx oldop = recog_operand[0];
if (need_slot (final_sequence))
@@ -702,20 +1153,7 @@ output_branch (logic, insn)
fprintf (asm_out_file, "\tb%c\tLF%d\n", logic ? 'f' : 't', label);
}
- recog_operand[0] = oldop;
-
- /* We use r13 as a scratch */
- need_save = 0;
- rn = 13;
-
- if (need_save)
- fprintf (asm_out_file, "\tpush r%d\n", rn);
- fprintf (asm_out_file, "\tmov.l LK%d,r%d\n", add_constant (oldop, SImode), rn);
- fprintf (asm_out_file, "\tjmp @r%d ! 32 cond \n", rn);
- if (need_save)
- fprintf (asm_out_file, "\tpop r%d\n", rn);
- else
- fprintf (asm_out_file, "\tor r0,r0\n");
+ output_far_jump (insn, oldop);
fprintf (asm_out_file, "LF%d:\n", label);
return "";
}
@@ -724,171 +1162,6 @@ output_branch (logic, insn)
}
-/* Predicates used by the templates */
-
-/* Non zero if op is an immediate ok for a byte index */
-
-int
-byte_index_operand (op, mode)
- rtx op;
- enum machine_mode mode;
-{
- return (GET_CODE (op) == CONST_INT
- && INTVAL (op) >= 0 && INTVAL (op) <= 15);
-}
-
-/* Non zero if OP is a pop operand */
-
-int
-pop_operand (op, mode)
- rtx op;
- enum machine_mode mode;
-{
- if (GET_CODE (op) != MEM)
- return 0;
-
- if (GET_MODE (op) != mode)
- return 0;
-
- op = XEXP (op, 0);
-
- if (GET_CODE (op) != POST_INC)
- return 0;
-
- return XEXP (op, 0) == stack_pointer_rtx;
-}
-
-/* Non zero if OP is an immediate which can be made from two insns. */
-
-int
-painful_immediate_operand (op, mode)
- rtx op;
- enum machine_mode mode;
-{
- if (GET_CODE (op) == CONST_INT)
- {
- int i = INTVAL (op);
-
- if (i > 127 && i < 255)
- return 1; /* two adds */
- }
- return 0;
-}
-
-
-/* Non zero if OP can be source of a simple move operation. */
-
-int
-general_movsrc_operand (op, mode)
- rtx op;
- enum machine_mode mode;
-{
- if (GET_CODE (op) == REG
- || GET_CODE (op) == SUBREG
- || (GET_CODE (op) == CONST_INT &&
- CONST_OK_FOR_I (INTVAL (op)))
- || GET_CODE (op) == MEM)
- return general_operand (op, mode);
- return 0;
-}
-
-
-
-/* Nonzero if OP is a normal arithmetic register. */
-
-int
-arith_reg_operand (op, mode)
- rtx op;
- enum machine_mode mode;
-{
- if (register_operand (op, mode))
- {
- if (GET_CODE (op) == REG)
- return REGNO (op) != T_REG;
- return 1;
- }
- return 0;
-}
-
-
-/* Nonzero if OP is a valid source operand for an arithmetic insn. */
-
-int
-arith_operand (op, mode)
- rtx op;
- enum machine_mode mode;
-{
- if (register_operand (op, mode))
- return 1;
-
- if (GET_CODE (op) == CONST_INT)
- {
- if (CONST_OK_FOR_I (INTVAL (op)))
- return 1;
- }
- return 0;
-}
-
-
-/* Nonzero if OP is a valid source operand for a logical operation */
-
-int
-logical_operand (op, mode)
- rtx op;
- enum machine_mode mode;
-{
- if (register_operand (op, mode))
- return 1;
-
- if (GET_CODE (op) == CONST_INT)
- {
- if (CONST_OK_FOR_L (INTVAL (op)))
- return 1;
- }
- return 0;
-}
-
-/* Nonzero if p is a valid shift operand for lshr and ashl */
-
-int
-ok_shift_value (p)
- rtx p;
-{
- if (GET_CODE (p) == CONST_INT)
- {
- switch (INTVAL (p))
- {
- case 1:
- case 2:
- case 8:
- case 16:
- return 1;
- default:
- if (TARGET_FASTCODE)
- return INTVAL (p) >= 0;
- }
- }
- return 0;
-}
-
-/* Nonzero if the arg is an immediate which has to be loaded from
- memory */
-
-int
-hard_immediate_operand (op, mode)
- rtx op;
- enum machine_mode mode;
-{
- if (immediate_operand (op, mode))
- {
- if (GET_CODE (op) == CONST_INT
- && INTVAL (op) >= -128 && INTVAL (op) < 127)
- return 0;
- return 1;
- }
- return 0;
-}
-
/* The SH cannot load a large constant into a register, constants have to
come from a pc relative load. The reference of a pc relative load
instruction must be less than 1k infront of the instruction. This
@@ -898,57 +1171,61 @@ hard_immediate_operand (op, mode)
It is important to minimize this, since the branches will slow things
down and make things bigger.
- Worst case code looks like:
-
- mov.l L1,rn
- bra L2
- nop
- align
-L1: .long value
-L2:
- ..
-
- mov.l L3,rn
- bra L4
- nop
- align
-L3: .long value
-L4:
- ..
-
- During shorten_branches we notice the instructions which can have a
- constant table in them, if we see two that are close enough
- together, we move the constants from the first table to the second
- table and continue. This process can happen again and again, and
- in the best case, moves the constant table outside of the function.
+ Worst case code looks like:
+
+ mov.l L1,rn
+ bra L2
+ nop
+ align
+ L1: .long value
+ L2:
+ ..
+
+ mov.l L3,rn
+ bra L4
+ nop
+ align
+ L3: .long value
+ L4:
+ ..
+
+ We fix this by performing a scan before scheduling, which notices which
+ instructions need to have their operands fetched from the constant table
+ and builds the table.
+
+
+ The algorithm is:
+
+ scan, find an instruction which needs a pcrel move. Look forward, find the
+ last barrier which is within MAX_COUNT bytes of the requirement.
+ If there isn't one, make one. Process all the instructions between
+ the find and the barrier.
In the above example, we can tell that L3 is within 1k of L1, so
the first move can be shrunk from the 3 insn+constant sequence into
just 1 insn, and the constant moved to L3 to make:
- mov.l L1,rn
+ mov.l L1,rn
..
- mov.l L3,rn
- bra L4
+ mov.l L3,rn
+ bra L4
nop
align
-L3:.long value
-L4:.long value
+ L3:.long value
+ L4:.long value
Then the second move becomes the target for the shortening process.
- We keep a simple list of all the constants accumulated in the
- current pool so there are no duplicates in a single table, but
- they are not factored into the size estimates.
-
-*/
+ */
typedef struct
{
- rtx value;
- int number;
- enum machine_mode mode;
-} pool_node;
+ rtx value; /* Value in table */
+ rtx label; /* Label of value */
+ enum machine_mode mode; /* Mode of value */
+}
+
+pool_node;
/* The maximum number of constants that can fit into one pool, since
the pc relative range is 0...1020 bytes and constants are at least 4
@@ -958,28 +1235,19 @@ typedef struct
static pool_node pool_vector[MAX_POOL_SIZE];
static int pool_size;
+/* Add a constant to the pool and return its label. */
-/* Add a constant to the pool and return its label number. */
-
-static int
+static rtx
add_constant (x, mode)
rtx x;
enum machine_mode mode;
{
int i;
-
- /* Start the countdown on the first constant */
-
- if (!pool_size)
- {
- first_pc = pc;
- }
-
+ rtx lab;
/* First see if we've already got it */
for (i = 0; i < pool_size; i++)
{
-
if (x->code == pool_vector[i].value->code
&& mode == pool_vector[i].mode)
{
@@ -989,240 +1257,20 @@ add_constant (x, mode)
continue;
}
}
-
if (rtx_equal_p (x, pool_vector[i].value))
- return pool_vector[i].number;
+ return pool_vector[i].label;
}
+ /* Need a new one */
pool_vector[pool_size].value = x;
+ lab = gen_label_rtx ();
pool_vector[pool_size].mode = mode;
- pool_vector[pool_size].number = lf;
+ pool_vector[pool_size].label = lab;
pool_size++;
-
- return lf++;
-}
-
-/* Nonzero if the insn could take a constant table. */
-
-static int
-has_constant_table (insn)
- rtx insn;
-{
- rtx body;
-
- if (GET_CODE (insn) == NOTE
- || GET_CODE (insn) == BARRIER
- || GET_CODE (insn) == CODE_LABEL)
- return 0;
-
- body = PATTERN (insn);
- if (GET_CODE (body) == SEQUENCE)
- return 0;
- if (GET_CODE (body) == ADDR_VEC)
- return 0;
- if (GET_CODE (body) == USE)
- return 0;
- if (GET_CODE (body) == CLOBBER)
- return 0;
- if (get_attr_constneed (insn) == CONSTNEED_YES)
- return 1;
-
- if (GET_CODE (body) == UNSPEC_VOLATILE)
- {
- return INTVAL (XVECEXP (body, 0, 0)) == 1;
- }
- return 0;
-}
-
-/* Adjust the length of an instruction.
-
- We'll look at the previous instruction which holds a constant
- table and see if we can move the table to here instead. */
-
-int target_insn_uid;
-int target_insn_smallest_size;
-
-int target_pc;
-int target_insn_range;
-int current_pc;
-int pool_bytes;
-
-int last_uid;
-int last_pc;
-
-void
-adjust_insn_length (insn, insn_lengths)
- rtx insn;
- short *insn_lengths;
-{
- int uid = INSN_UID (insn);
- rtx body = PATTERN (insn);
-
- current_pc += insn_lengths[uid];
-
-
- if (GET_CODE (body) == SEQUENCE)
- {
- int i;
-
- for (i = 0; i < XVECLEN (body, 0); i++)
- {
- adjust_insn_length (XVECEXP (body, 0, i), insn_lengths);
- }
- }
- else
- {
- if (has_constant_table (insn))
- {
- if (current_pc >= target_insn_range)
- {
- /* This instruction is further away from the referencing
- instruction than it can reach, so we'll stop accumulating
- from that one and start fresh. */
- target_pc = current_pc;
- target_insn_range = current_pc + MAYBE_DUMP_LEVEL;
- }
- else
- {
- /* This instruction is within the reach of the target,
- remove the constant table from the target by adjusting
- downwards, and increase the size of this one to
- compensate. */
-
-
- /* Add the stuff from this insn to what will go in the
- growing table. */
-
- pool_bytes += get_attr_constantsize (insn);
-
- /* The target shinks to its smallest natural size */
- insn_lengths[target_insn_uid] = target_insn_smallest_size;
-
- /* The current insn grows to be its larger size plust the
- table size. */
-
- insn_lengths[uid] = get_attr_largestsize (insn) + pool_bytes;
-
- }
- /* Current insn becomes the target. */
- target_insn_uid = uid;
- target_insn_smallest_size = get_attr_smallestsize (insn);
-
- }
- }
+ return lab;
}
-
-
-/* Dump out the pending constant pool.
- If label provided then insert an branch in the middle of the table
- */
-
-int
-dump_constants (label)
-{
- int i;
- int rlabel = label;
- int size = 0;
-
- if (pool_size)
- {
- fprintf (asm_out_file, "\n\t! constants - waited %d\n", pc - first_pc);
- fprintf (asm_out_file, "\t.align\t2\n");
-
- for (i = 0; i < pool_size; i++)
- {
- pool_node *p = pool_vector + i;
-
- fprintf (asm_out_file, "LK%d:", p->number);
- size += GET_MODE_SIZE (p->mode);
-
- switch (GET_MODE_CLASS (p->mode))
- {
- case MODE_INT:
- case MODE_PARTIAL_INT:
- assemble_integer (p->value, GET_MODE_SIZE (p->mode), 1);
- break;
- case MODE_FLOAT:
- {
- union real_extract u;
- bcopy (&CONST_DOUBLE_LOW (p->value), &u, sizeof u);
- assemble_real (u.d, p->mode);
- }
- }
-
- /* After 200 bytes of table, stick in another branch */
- if (label && size > 200)
- {
- rlabel = lf++;
- fprintf (asm_out_file, "LF%d:\tbra LF%d\n", label, rlabel);
- fprintf (asm_out_file, "\tor r0,r0\n");
- label = 0;
- }
-
- }
- }
-
- pool_size = 0;
- current_pc = 0;
- pc = 0;
- pool_bytes = 0;
-
- target_insn_range = 0;
- return rlabel;
-
-}
-
-
-/* Emit the text to load a value from a constant table. */
-
-char *
-output_movepcrel (insn, operands, mode)
- rtx insn;
- rtx operands[];
- enum machine_mode mode;
-{
- int len = GET_MODE_SIZE (mode);
- int rn = REGNO (operands[0]);
-
- fprintf (asm_out_file, "\tmov.l LK%d,r%d\n",
- add_constant (operands[1], mode), rn);
-
- if (GET_MODE_SIZE (mode) > 4)
- {
- fprintf (asm_out_file,
- "\tmov.l LK%d+4,r%d\n",
- add_constant (operands[1], mode),
- rn + 1);
-
- }
-
- /* This may have been the last move in the function, so nothing
- took its constant table, we may be able to move it past the end
- of the function (after the rts) if we are careful */
-
- if (target_insn_uid == INSN_UID (insn)
- && current_pc < target_insn_range)
- return "";
-
-
- /* If this instruction is as small as it can be, there can be no
- constant table attached to it. */
- if (get_attr_length (insn) != get_attr_smallestsize (insn))
- {
- /* This needs a constant table */
- fprintf (asm_out_file, "\t!constant table start\n");
- fprintf (asm_out_file, "\tbra LF%d\n", lf);
- fprintf (asm_out_file, "\tor r0,r0 ! wasted slot\n");
- dump_constants (0);
- fprintf (asm_out_file, "LF%d:\n", lf++);
- fprintf (asm_out_file, "\t!constant table end\n");
- }
- return "";
-}
-
-
/* Dump out interesting debug info */
rtx
@@ -1231,192 +1279,15 @@ final_prescan_insn (insn, opvec, noperands)
rtx *opvec;
int noperands;
{
- register rtx body = PATTERN (insn);
-
if (target_flags & ISIZE_BIT)
{
extern int *insn_addresses;
-
- fprintf (asm_out_file, "\n!%04x*\n",
- insn_addresses[INSN_UID (insn)] + 0x10);
-
- fprintf (asm_out_file, "\n!%04x %d %04x len=%d\n",
- pc, pool_size, first_pc, get_attr_length (insn));
-
- if (TARGET_DUMP_RTL)
- print_rtl (asm_out_file, body);
-
-
- }
-
- pc += get_attr_length (insn);
- if (pool_size && pc - first_pc > MUST_DUMP_LEVEL)
- {
- /* For some reason we have not dumped out a constant table, and
- we have emitted a lot of code. This can happen if the think
- which wants the table is a long conditional branch (which has no
- room for a constant table), and there has not been a move
- constant anywhere. */
- int label = lf++;
- fprintf (asm_out_file, "\t!forced constant table\n");
- fprintf (asm_out_file, "\tbra LF%d\n", label);
- fprintf (asm_out_file, "\tor r0,r0 ! wasted slot\n");
- label = dump_constants (label);
- fprintf (asm_out_file, "LF%d:\n", label);
- fprintf (asm_out_file, "\t!constant table end\n");
- }
-}
-
-
-
-/* Block move stuff stolen from m88k*/
-
-/* Emit code to perform a block move. Choose the best method.
-
- OPERANDS[0] is the destination.
- OPERANDS[1] is the source.
- OPERANDS[2] is the size.
- OPERANDS[3] is the alignment safe to use. */
-
-/* Emit code to perform a block move with an offset sequence of ld/st
- instructions (..., ld 0, st 1, ld 1, st 0, ...). SIZE and ALIGN are
- known constants. DEST and SRC are registers. OFFSET is the known
- starting point for the output pattern. */
-
-static enum machine_mode mode_from_align[] =
-{VOIDmode, QImode, HImode, VOIDmode, SImode,
- VOIDmode, VOIDmode, VOIDmode, DImode};
-static void
-
-block_move_sequence (dest, dest_mem, src, src_mem, size, align, offset)
- rtx dest, dest_mem;
- rtx src, src_mem;
- int size;
- int align;
- int offset;
-{
- rtx temp[2];
- enum machine_mode mode[2];
- int amount[2];
- int active[2];
- int phase = 0;
- int next;
- int offset_ld = offset;
- int offset_st = offset;
-
- active[0] = active[1] = FALSE;
-
- /* Establish parameters for the first load and for the second load if
- it is known to be the same mode as the first. */
- amount[0] = amount[1] = align;
-
-
- mode[0] = mode_from_align[align];
-
- temp[0] = gen_reg_rtx (mode[0]);
- if (size >= 2 * align)
- {
- mode[1] = mode[0];
- temp[1] = gen_reg_rtx (mode[1]);
+ fprintf (asm_out_file, "\n! at %04x\n",
+ insn_addresses[INSN_UID (insn)]);
}
-
- do
- {
- rtx srcp, dstp;
- next = phase;
- phase = !phase;
-
- if (size > 0)
- {
- /* Change modes as the sequence tails off. */
- if (size < amount[next])
- {
- amount[next] = (size >= 4 ? 4 : (size >= 2 ? 2 : 1));
- mode[next] = mode_from_align[amount[next]];
- temp[next] = gen_reg_rtx (mode[next]);
- }
- size -= amount[next];
- srcp = gen_rtx (MEM,
- MEM_IN_STRUCT_P (src_mem) ? mode[next] : BLKmode,
- gen_rtx (PLUS, Pmode, src,
- gen_rtx (CONST_INT, SImode, offset_ld)));
- RTX_UNCHANGING_P (srcp) = RTX_UNCHANGING_P (src_mem);
- MEM_VOLATILE_P (srcp) = MEM_VOLATILE_P (src_mem);
- MEM_IN_STRUCT_P (srcp) = 1;
- emit_insn (gen_rtx (SET, VOIDmode, temp[next], srcp));
- offset_ld += amount[next];
- active[next] = TRUE;
- }
-
- if (active[phase])
- {
- active[phase] = FALSE;
- dstp = gen_rtx (MEM,
- MEM_IN_STRUCT_P (dest_mem) ? mode[phase] : BLKmode,
- gen_rtx (PLUS, Pmode, dest,
- gen_rtx (CONST_INT, SImode, offset_st)));
- RTX_UNCHANGING_P (dstp) = RTX_UNCHANGING_P (dest_mem);
- MEM_VOLATILE_P (dstp) = MEM_VOLATILE_P (dest_mem);
- MEM_IN_STRUCT_P (dstp) = 1;
- emit_insn (gen_rtx (SET, VOIDmode, dstp, temp[phase]));
- offset_st += amount[phase];
- }
- }
- while (active[next]);
}
-void
-expand_block_move (dest_mem, src_mem, operands)
- rtx dest_mem;
- rtx src_mem;
- rtx *operands;
-{
- int align = INTVAL (operands[3]);
- int constp = (GET_CODE (operands[2]) == CONST_INT);
- int bytes = (constp ? INTVAL (operands[2]) : 0);
-
-#if 0
- if (constp && bytes <= 0)
- return;
- if (align > 4)
- align = 4;
-
- if (constp && bytes <= 3 * align)
- block_move_sequence (operands[0], dest_mem, operands[1], src_mem,
- bytes, align, 0);
-
-#if 0
- else if (constp && bytes <= best_from_align[target][align])
- block_move_no_loop (operands[0], dest_mem, operands[1], src_mem,
- bytes, align);
-
- else if (constp && align == 4 && TARGET_88100)
- block_move_loop (operands[0], dest_mem, operands[1], src_mem,
- bytes, align);
-#endif
- else
-#endif
- {
- emit_library_call (gen_rtx (SYMBOL_REF, Pmode, "memcpy"), 0,
- VOIDmode, 3,
- operands[0], Pmode,
- operands[1], Pmode,
- operands[2], SImode);
- }
-}
-
-
-override_options ()
-{
- sh_cpu = CPU_SH0;
- if (TARGET_SH1)
- sh_cpu = CPU_SH1;
- if (TARGET_SH2)
- sh_cpu = CPU_SH2;
- if (TARGET_SH3)
- sh_cpu = CPU_SH3;
-}
/* Stuff taken from m88k.c */
@@ -1449,10 +1320,10 @@ output_option (file, sep, type, name, indent, pos, max)
}
static struct
- {
- char *name;
- int value;
- }
+{
+ char *name;
+ int value;
+}
m_options[] = TARGET_SWITCHES;
@@ -1503,6 +1374,7 @@ output_options (file, f_options, f_len, W_options, W_len,
fprintf (file, term);
+ fprintf (file, "! %d %d\n", max_count_si, max_count_hi);
}
void
@@ -1527,8 +1399,515 @@ output_file_start (file, f_options, f_len, W_options, W_len)
}
+
+/* Return the cost of a shift */
+
+int
+shiftcosts (RTX)
+ rtx RTX;
+{
+ /* If shift by a non constant, then this will be expensive. */
+ if (GET_CODE (XEXP (RTX, 1)) != CONST_INT)
+ return 20;
+
+ /* otherwise, it will be very cheap if by one of the constants
+ we can cope with. */
+ if (CONST_OK_FOR_K (INTVAL (XEXP (RTX, 1))))
+ return 1;
+
+ /* otherwise it will be several insns, but we pretend that it will be more than
+ just the components, so that combine doesn't glue together a load of shifts into
+ one shift which has to be emitted as a bunch anyway - breaking scheduling */
+ return 100;
+}
+
+int
+andcosts (RTX)
+ rtx RTX;
+{
+ int i;
+ if (GET_CODE (XEXP (RTX, 1)) != CONST_INT)
+ return 2;
+ i = INTVAL (XEXP (RTX, 1));
+ /* And can use the extend insns cheaply */
+ if (i == 0xff || i == 0xffff)
+ return 2;
+ /* Any small constant is reasonably cheap - but requires r0 */
+ if (CONST_OK_FOR_I (i))
+ return 3;
+ return 5;
+}
+/* Return the cost of a multiply */
+int
+multcosts (RTX)
+ rtx RTX;
+{
+ if (TARGET_SH2)
+ return 2;
+ /* If we we're aiming at small code, then just count the number of
+ insns in a multiply call sequence, otherwise, count all the insnsn
+ inside the call. */
+ if (TARGET_SMALLCODE)
+ return 3;
+ return 30;
+}
+
+/* Code to expand a shift */
+
+void
+gen_ashift (type, n, reg)
+ int type;
+ int n;
+ rtx reg;
+{
+ switch (type)
+ {
+ case ASHIFTRT:
+ emit_insn (gen_ashrsi3_k (reg, reg, GEN_INT (n)));
+ break;
+ case LSHIFTRT:
+ emit_insn (gen_lshrsi3_k (reg, reg, GEN_INT (n)));
+ break;
+ case ASHIFT:
+ if (n == 1)
+ emit_insn (gen_addsi3 (reg, reg, reg));
+ else
+ emit_insn (gen_ashlsi3_k (reg, reg, GEN_INT (n)));
+ break;
+ }
+}
+
+int
+gen_shifty_op (code, operands)
+ int code;
+ rtx *operands;
+{
+ rtx wrk = gen_reg_rtx (SImode);
+ rtx t;
+ char *func;
+ if (GET_CODE (operands[2]) == CONST_INT)
+ {
+ int value = INTVAL (operands[2]);
+ top:
+ switch (code)
+ {
+ case ASHIFTRT:
+ if (value < 0)
+ {
+ code = ASHIFT;
+ value = -value;
+ goto top;
+ }
+
+ /* Expand a short sequence inline, longer call a magic routine */
+ if (value < 4)
+ {
+ emit_move_insn (wrk, operands[1]);
+ while (value--)
+ {
+ gen_ashift (ASHIFTRT, 1, wrk);
+ }
+ emit_move_insn (operands[0], wrk);
+ return 1;
+ }
+ t = gen_reg_rtx (Pmode);
+ /* Load the value into an arg reg and call a helper */
+ emit_move_insn (gen_rtx (REG, SImode, 4), operands[1]);
+ if (!shiftsyms[value])
+ {
+ func = xmalloc (18);
+ sprintf (func, "__ashiftrt_r4_%d", value);
+ shiftsyms[value] = gen_rtx (SYMBOL_REF, Pmode, func);
+ }
+ emit_move_insn (t, shiftsyms[value]);
+ emit_insn (gen_ashrsi3_n (GEN_INT (value), t));
+ emit_move_insn (operands[0], gen_rtx (REG, SImode, 4));
+ return 1;
+
+ case ASHIFT:
+ if (value < 0)
+ {
+ code = LSHIFTRT;
+ value = -value;
+ goto top;
+ }
+ /* Fall through */
+ case LSHIFTRT:
+
+ if (value < 0)
+ {
+ code = ASHIFT;
+ value = -value;
+ goto top;
+ }
+
+ emit_move_insn (wrk, operands[1]);
+ while (value)
+ {
+ if (value >= 16)
+ {
+ gen_ashift (code, 16, wrk);
+ value -= 16;
+ }
+ else if (value >= 8)
+ {
+ gen_ashift (code, 8, wrk);
+ value -= 8;
+ }
+ else if (value >= 2)
+ {
+ gen_ashift (code, 2, wrk);
+ value -= 2;
+ }
+ else
+ {
+ gen_ashift (code, 1, wrk);
+ value--;
+ }
+ }
+ emit_move_insn (operands[0], wrk);
+ return 1;
+
+ }
+ }
+ return 0;
+}
+
+/* Dump out any constants accumulated in the final pass -
+ which will only be labels */
+char *
+output_jump_label_table ()
+{
+ int i;
+ if (pool_size)
+ {
+ fprintf (asm_out_file, "\t.align 2\n");
+ for (i = 0; i < pool_size; i++)
+ {
+ pool_node *p = pool_vector + i;
+
+ ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "L", CODE_LABEL_NUMBER (p->label));
+ output_asm_insn (".long %O0", &p->value);
+ }
+ pool_size = 0;
+ }
+
+ return "";
+}
+/* Output the literal table */
+
+static void
+dump_table (scan)
+ rtx scan;
+{
+ int i;
+ int pass;
+ int need_align = 1;
+
+
+ /* Do two passes, first time dump out the HI sized constants */
+
+ for (i = 0; i < pool_size; i++)
+ {
+ pool_node *p = pool_vector + i;
+ if (p->mode == HImode)
+ {
+ if (need_align)
+ {
+ scan = emit_insn_after (gen_align_2 (), scan);
+ need_align = 0;
+ }
+ scan = emit_label_after (p->label, scan);
+ scan = emit_insn_after (gen_consttable_2 (p->value), scan);
+ }
+ }
+ need_align = 1;
+
+
+ for (i = 0; i < pool_size; i++)
+ {
+ pool_node *p = pool_vector + i;
+
+ switch (p->mode)
+ {
+ case HImode:
+ break;
+ case SImode:
+ if (need_align)
+ {
+ need_align = 0;
+ scan = emit_insn_after (gen_align_4 (), scan);
+ }
+ scan = emit_label_after (p->label, scan);
+ scan = emit_insn_after (gen_consttable_4 (p->value), scan);
+ break;
+ case DImode:
+ if (need_align)
+ {
+ need_align = 0;
+ scan = emit_insn_after (gen_align_4 (), scan);
+ }
+ scan = emit_label_after (p->label, scan);
+ scan = emit_insn_after (gen_consttable_8 (p->value), scan);
+ break;
+ default:
+ abort ();
+ break;
+ }
+ }
+
+ scan = emit_insn_after (gen_consttable_end (), scan);
+ scan = emit_barrier_after (scan);
+ pool_size = 0;
+}
+
+
+
+/* Non zero if the src operand needs to be fixed up */
+static
+int
+fixit (src, mode)
+ rtx src;
+ enum machine_mode mode;
+{
+ if (mode == QImode)
+ return 0; /* QIs never need to be fixed */
+ if (GET_CODE (src) == CONST)
+ return 1;
+
+ if (GET_CODE (src) == SYMBOL_REF)
+ {
+ return 1;
+ }
+ if (GET_CODE (src) == CONST_INT)
+ {
+ /* All QI insns are ok */
+ if (mode == QImode)
+ return 1;
+ /* The rest may need to be fixed */
+ return !CONST_OK_FOR_I (INTVAL (src));
+ }
+ return 0;
+}
+
+/* Return Non-zero if constant would be an ok source for a
+ mov.w instead of a mov.l */
+int
+hi_const (src)
+ rtx src;
+{
+ return (GET_CODE (src) == CONST_INT
+ && INTVAL (src) >= -32768
+ && INTVAL (src) <= 32767);
+}
+
+/* Find the last barrier less than MAX_COUNT bytes from FROM, or create one.
+ If an HI move is found, then make sure that MAX_COUNT_HI isn't broken from that one. */
+
+static
+rtx
+find_barrier (from)
+ rtx from;
+{
+ int count_si = 0;
+ int count_hi = 0;
+ int found_hi = 0;
+ int found_si = 0;
+ rtx found_barrier = 0;
+
+ while (from
+ && count_si < max_count_si
+ && count_hi < max_count_hi)
+ {
+ int inc;
+ if (GET_CODE (from) == BARRIER)
+ {
+ found_barrier = from;
+ }
+ /* Count the length of this insn - we assume that all the pcrelloads
+ will work out to be only 2 bytes long */
+
+ if (GET_CODE (from) == INSN &&
+ GET_CODE (PATTERN (from)) == SET)
+ {
+ rtx src = SET_SRC (PATTERN (from));
+ if (hi_const (src))
+ found_hi = 1;
+ else
+ found_si = 1;
+ inc = 2;
+ }
+ else
+ {
+ inc = get_attr_length (from);
+ }
+ if (found_si)
+ count_si += inc;
+ if (found_hi)
+ count_hi += inc;
+ from = NEXT_INSN (from);
+ }
+
+ if (!found_barrier)
+ {
+ /* Insert a jump around the barrier here */
+ rtx label = gen_label_rtx ();
+ /* Walk back to be just before any jump */
+ while (GET_CODE (from) == JUMP_INSN
+ || GET_CODE (from) == NOTE)
+ {
+ from = PREV_INSN (from);
+ }
+ from = emit_jump_insn_after (gen_jump (label), from);
+ JUMP_LABEL (from) = label;
+ found_barrier = emit_barrier_after (from);
+ emit_label_after (label, found_barrier);
+ return found_barrier;
+ }
+ return found_barrier;
+}
+
+/* Non zero if the insn is a move instruction which needs to be fixed. */
+
+static
+int
+broken_move (insn)
+ rtx insn;
+{
+ if (!INSN_DELETED_P (insn)
+ && GET_CODE (insn) == INSN
+ && GET_CODE (PATTERN (insn)) == SET)
+ {
+ rtx pat = PATTERN (insn);
+ rtx src = SET_SRC (pat);
+ rtx dst = SET_DEST (pat);
+ enum machine_mode mode = GET_MODE (dst);
+ if (dst == pc_rtx)
+ return 0;
+ return fixit (src, mode);
+ }
+ return 0;
+}
+
+
+/* Exported to toplev.c
+
+ Scan the function looking for move instructions which have to be changed to
+ pcrel loads and insert the literal tables. */
+
+void
+machine_dependent_reorg (first)
+ rtx first;
+{
+ rtx insn;
+ int limit;
+ for (insn = first; insn; insn = NEXT_INSN (insn))
+ {
+ if (broken_move (insn))
+ {
+ /* This is a broken move instruction, scan ahead looking for
+ a barrier to stick the constant table behind */
+ rtx scan;
+ rtx barrier = find_barrier (insn);
+
+ /* Now find all the moves between the points and modify them */
+ for (scan = insn; scan != barrier; scan = NEXT_INSN (scan))
+ {
+ if (broken_move (scan))
+ {
+ rtx pat = PATTERN (scan);
+ rtx src = SET_SRC (pat);
+ rtx dst = SET_DEST (pat);
+ enum machine_mode mode = GET_MODE (dst);
+ rtx lab;
+ rtx newinsn;
+ rtx newsrc;
+ /* This is a broken move instruction, add it to the pool */
+
+ if (mode == SImode && hi_const (src))
+ {
+ /* This is an HI source, clobber the dest to get the mode right too */
+ mode = HImode;
+ dst = gen_rtx (REG, HImode, REGNO (dst));
+ }
+ lab = add_constant (src, mode);
+ newsrc = gen_rtx (MEM, mode,
+ gen_rtx (LABEL_REF, VOIDmode, lab));
+
+ /* Build a jump insn wrapper around the move instead
+ of an ordinary insn, because we want to have room for
+ the target label rtx in fld[7], which an ordinary
+ insn doesn't have. */
+ newinsn = emit_jump_insn_after (gen_rtx (SET, VOIDmode,
+ dst, newsrc), scan);
+ JUMP_LABEL (newinsn) = lab;
+
+ /* But it's still an ordinary insn */
+ PUT_CODE (newinsn, INSN);
+
+ /* Kill old insn */
+ delete_insn (scan);
+ scan = newinsn;
+ }
+ }
+ dump_table (barrier);
+ }
+ }
+}
+
+/* Called from the md file, set up the operands of a compare instruction */
+
+int
+from_compare (operands, code)
+ rtx *operands;
+ int code;
+{
+ operands[1] = sh_compare_op0;
+ operands[2] = force_reg (SImode, sh_compare_op1);
+ operands[1] = force_reg (SImode, operands[1]);
+}
+
+/* Non-zero if x is EQ or NE */
+
+int
+equality_operator (x, mode)
+ rtx x;
+ enum machine_mode mode;
+{
+ enum rtx_code code = GET_CODE (x);
+ return (code == EQ || code == NE);
+}
+
+
+/* Framefull frame looks like:
+
+ arg-5
+ arg-4
+ [ if current_function_anonymous_args
+ arg-3
+ arg-2
+ arg-1
+ arg-0 ]
+ saved-fp
+ saved-r10
+ saved-r11
+ saved-r12
+ saved-pr
+ local-n
+ ..
+ local-1
+ local-0 <- fp points here
+
+
+ If TARGET_SMALLCALL, then the preserved registers are pushed by a
+ wrapper before the routine is entered, so the regs are always pushed
+ and there are two pr's on the stack - the caller and the wrapper.
+ */
+
+
/* Code to generate prologue and epilogue sequences */
+
void
sh_expand_prologue ()
{
@@ -1537,7 +1916,9 @@ sh_expand_prologue ()
live_regs_mask = calc_live_regs (&d);
- output_stack_adjust (-1, current_function_pretend_args_size);
+ /* We have pretend args if we had an object sent partially in registers
+ and partially on the stack - eg a large structure */
+ output_stack_adjust (-current_function_pretend_args_size);
if (current_function_anonymous_args)
{
@@ -1549,22 +1930,16 @@ sh_expand_prologue ()
if (i > NPARM_REGS - current_function_args_info)
break;
push (rn);
-
extra_push += 4;
}
}
+ push_regs (live_regs_mask);
+ output_stack_adjust (-get_frame_size ());
if (frame_pointer_needed)
{
- push_regs (live_regs_mask);
emit_insn (gen_movsi (frame_pointer_rtx, stack_pointer_rtx));
}
- else
- {
- push_regs (live_regs_mask);
- }
-
- output_stack_adjust (-1, get_frame_size ());
}
void
@@ -1580,13 +1955,10 @@ sh_expand_epilogue ()
{
emit_insn (gen_movsi (stack_pointer_rtx, frame_pointer_rtx));
}
- else
- {
- output_stack_adjust (1, get_frame_size ());
- }
-
+ output_stack_adjust (get_frame_size ());
/* Pop all the registers */
+
for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
{
int j = (FIRST_PSEUDO_REGISTER - 1) - i;
@@ -1595,43 +1967,285 @@ sh_expand_epilogue ()
pop (j);
}
}
- output_stack_adjust (1, extra_push +
- current_function_pretend_args_size);
- extra_push = 0;
+ output_stack_adjust (extra_push + current_function_pretend_args_size);
+ extra_push = 0;
+ current_function_pretend_args_size = 0;
current_function_anonymous_args = 0;
+ for (i = 0; i < 32; i++)
+ shiftsyms[i] = 0;
}
+/* Define the offset between two registers, one to be eliminated, and
+ the other its replacement, at the start of a routine. */
-/* Return the cost of a shift */
+int
+initial_elimination_offset (from, to)
+{
+ int regs_saved;
+ int regs_saved_mask = calc_live_regs (&regs_saved);
+ int total_saved_regs_space;
+ int total_auto_space = get_frame_size ();
+ total_saved_regs_space = (regs_saved) * 4;
+
+ if (from == ARG_POINTER_REGNUM && to == FRAME_POINTER_REGNUM)
+ {
+ return total_saved_regs_space + total_auto_space;
+ }
+ if (from == ARG_POINTER_REGNUM && to == STACK_POINTER_REGNUM)
+ {
+ return total_saved_regs_space + total_auto_space;
+ }
+ if (from == FRAME_POINTER_REGNUM && to == STACK_POINTER_REGNUM)
+ {
+ /* Initial gap between fp and sp is 0 */
+ return 0;
+ }
+ abort ();
+}
+
+/* Handle machine specific pragmas to be semi-compatible with Hitachi
+ compiler */
int
-shiftcosts (RTX)
- rtx RTX;
+handle_pragma (file)
+ FILE *file;
{
- /* If shift by a non constant, then this will be expensive. */
- if (GET_CODE (XEXP (RTX, 1)) != CONST_INT)
- return 20;
+ int c;
+ char pbuf[200];
+ int psize = 0;
- /* otherwise, it will be very cheap if by one of the constants
- we can cope with. */
- if (CONST_OK_FOR_K (INTVAL (XEXP (RTX, 1))))
+ c = getc (file);
+ while (c == ' ' || c == '\t')
+ c = getc (file);
+
+ if (c == '\n' || c == EOF)
+ return c;
+
+ while (psize < sizeof (pbuf) - 1 && c != '\n')
+ {
+ pbuf[psize++] = c;
+ if (psize == 9 && strncmp (pbuf, "interrupt", 9) == 0)
+ {
+ pragma_interrupt = 1;
+ return;
+ }
+ if (psize == 5 && strncmp (pbuf, "trapa", 5) == 0)
+ {
+ pragma_interrupt = pragma_trapa = 1;
+ return;
+ }
+ c = getc (file);
+ }
+ return c;
+}
+
+/* insn expand helpers */
+
+/* Emit insns to perform a call. If TARGET_SMALLCALL, then load the
+ target address into r1 and call __saveargs, otherwise
+ perform the standard call sequence */
+
+void
+expand_acall (isa_retval, operands)
+ int isa_retval;
+ rtx *operands;
+{
+ rtx call;
+ rtx ret = operands[0];
+ rtx call_target = operands[isa_retval + 0];
+ rtx numargs = operands[isa_retval + 1];
+
+ if (GET_CODE (call_target) == MEM)
+ {
+ call_target = force_reg (Pmode,
+ XEXP (call_target, 0));
+ }
+ if (TARGET_SMALLCALL)
+ {
+ rtx tmp = gen_reg_rtx (SImode);
+ rtx r1 = gen_rtx (REG, SImode, 1);
+ emit_move_insn (tmp, gen_rtx (SYMBOL_REF, SImode, "__saveargs"));
+ emit_move_insn (r1, call_target);
+ emit_insn (gen_rtx (USE, VOIDmode, r1));
+ call_target = tmp;
+ }
+
+ call = gen_rtx (CALL, VOIDmode, gen_rtx (MEM, SImode, call_target), numargs);
+
+ if (isa_retval)
+ {
+ call = gen_rtx (SET, VOIDmode, ret, call);
+ }
+
+ emit_call_insn (gen_rtx (PARALLEL, VOIDmode,
+ gen_rtvec (2,
+ call,
+ gen_rtx (CLOBBER, VOIDmode, gen_rtx (REG, SImode, 17)))));
+
+}
+
+
+/* Predicates used by the templates */
+
+
+/* Returns 1 if OP can be source of a simple move operation.
+ Same as general_operand, but a LABEL_REF is valid, PRE_DEC is
+ invalid as are subregs of system registers. */
+
+int
+general_movsrc_operand (op, mode)
+ rtx op;
+ enum machine_mode mode;
+{
+ /* Any MEM(label_ref) is ok, that's a pcrel load */
+ if (GET_CODE (op) == MEM &&
+ GET_CODE (XEXP (op, 0)) == LABEL_REF)
return 1;
- /* otherwise it will be several insns. */
- return 4;
+ /* No predec allowed */
+
+ if (GET_CODE (op) == MEM
+ && GET_CODE (XEXP (op, 0)) == PRE_DEC)
+ return 0;
+
+ if ((mode == QImode || mode == HImode)
+ && (GET_CODE (op) == SUBREG
+ && GET_CODE (XEXP (op, 0)) == REG
+ && system_reg_operand (XEXP (op, 0), mode)))
+ return 0;
+
+ if (GET_CODE (op) == CONST_INT)
+ {
+ int i = INTVAL (op);
+ return CONST_OK_FOR_I (i);
+ }
+ return general_operand (op, mode);
}
-/* Return the cost of a multiply */
+
+/* Returns 1 if OP can be a destination of a move.
+ Same as general_operand, but no preinc allowed. */
+
int
-multcosts (RTX)
- rtx RTX;
+general_movdst_operand (op, mode)
+ rtx op;
+ enum machine_mode mode;
{
- /* If we we're aiming at small code, then just count the number of
- insns in a multiply call sequence, otherwise, count all the insnsn
- inside the call. */
- if (TARGET_SMALLCODE)
- return 3;
- return 30;
+ if (GET_CODE (op) == MEM
+ && GET_CODE (XEXP (op, 0)) == PRE_INC)
+ return 0;
+ return general_operand (op, mode);
+}
+
+
+/* Returns 1 if OP is an immediate ok for a byte index. */
+
+int
+byte_index_operand (op, mode)
+ rtx op;
+ enum machine_mode mode;
+{
+ return (GET_CODE (op) == CONST_INT
+ && INTVAL (op) >= 0
+ && INTVAL (op) <= 15);
+}
+
+/* Returns 1 if OP is a pop operand. */
+
+int
+pop_operand (op, mode)
+ rtx op;
+ enum machine_mode mode;
+{
+ if (GET_CODE (op) != MEM)
+ return 0;
+
+ if (GET_MODE (op) != mode)
+ return 0;
+
+ op = XEXP (op, 0);
+
+ if (GET_CODE (op) != POST_INC)
+ return 0;
+
+ return XEXP (op, 0) == stack_pointer_rtx;
+}
+
+
+/* Returns 1 if OP is a normal arithmetic register. */
+
+int
+arith_reg_operand (op, mode)
+ rtx op;
+ enum machine_mode mode;
+{
+ if (register_operand (op, mode))
+ {
+ if (GET_CODE (op) == REG)
+ return (REGNO (op) != T_REG
+ && REGNO (op) != PR_REG);
+ return 1;
+ }
+ return 0;
}
+
+/* Returns 1 if OP is MACL, MACH or PR. */
+
+int
+system_reg_operand (op, mode)
+ rtx op;
+ enum machine_mode mode;
+{
+ if (GET_CODE (op) == REG)
+ {
+ switch (REGNO (op))
+ {
+ case PR_REG:
+ case MACL_REG:
+ case MACH_REG:
+ return 1;
+ }
+ }
+ return 0;
+}
+
+
+/* Returns 1 if OP is a valid source operand for an arithmetic insn. */
+
+int
+arith_operand (op, mode)
+ rtx op;
+ enum machine_mode mode;
+{
+ if (arith_reg_operand (op, mode))
+ return 1;
+
+ if (GET_CODE (op) == CONST_INT)
+ {
+ if (CONST_OK_FOR_I (INTVAL (op)))
+ return 1;
+ }
+ return 0;
+}
+
+
+/* Returns 1 if OP is a valid source operand for a logical operation. */
+
+int
+logical_operand (op, mode)
+ rtx op;
+ enum machine_mode mode;
+{
+ if (arith_reg_operand (op, mode))
+ return 1;
+
+ if (GET_CODE (op) == CONST_INT)
+ {
+ if (CONST_OK_FOR_L (INTVAL (op)))
+ return 1;
+ }
+ return 0;
+}
+
diff --git a/gcc/config/sh/sh.h b/gcc/config/sh/sh.h
index d59e8ec..74094f1 100644
--- a/gcc/config/sh/sh.h
+++ b/gcc/config/sh/sh.h
@@ -1,5 +1,5 @@
/* Definitions of target machine for GNU compiler, for Hitachi Super-H.
- Copyright (C) 1993 Free Software Foundation, Inc.
+ Copyright (C) 1993, 1994 Free Software Foundation, Inc.
Contributed by Steve Chamberlain (sac@cygnus.com)
@@ -36,24 +36,48 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
/* Show we can debug even without a frame pointer. */
#define CAN_DEBUG_WITHOUT_FP
+
+
+#define CONDITIONAL_REGISTER_USAGE \
+ /* Experimental calling convention with fewer saved registers */ \
+ if (TARGET_NOSAVE) \
+ { \
+ call_used_regs[8] = 1; \
+ call_used_regs[9] = 1; \
+ call_used_regs[10] = 1; \
+ call_used_regs[11] = 1; \
+ } \
+ /* Hitachi saves and restores mac registers on call */ \
+ if (TARGET_HITACHI) \
+ { \
+ call_used_regs[MACH_REG] = 0; \
+ call_used_regs[MACL_REG] = 0; \
+ }
+
/* Run-time compilation parameters selecting different hardware subsets. */
extern int target_flags;
-#define ISIZE_BIT 1
-#define FAST_BIT 2
-
-#define MAC_BIT 8
-#define RTL_BIT 16
-#define DT_BIT 32
-#define DALIGN_BIT 64
-#define SH0_BIT 128
-#define SH1_BIT 256
-#define SH2_BIT 512
-#define SH3_BIT 1024
-#define C_BIT 2048
-#define R_BIT (1<<12)
-#define SPACE_BIT (1<<13)
+#define ISIZE_BIT (1<<1)
+#define FAST_BIT (1<<2)
+#define MAC_BIT (1<<3)
+#define RTL_BIT (1<<4)
+#define DT_BIT (1<<5)
+#define DALIGN_BIT (1<<6)
+#define SH0_BIT (1<<7)
+#define SH1_BIT (1<<8)
+#define SH2_BIT (1<<9)
+#define SH3_BIT (1<<10)
+#define C_BIT (1<<11)
+#define R_BIT (1<<12)
+#define SPACE_BIT (1<<13)
+#define BIGTABLE_BIT (1<<14)
+#define TRYR0_BIT (1<<15)
+#define NOSAVE_BIT (1<<16)
+#define SMALLCALL_BIT (1<<17)
+#define CONSTLEN_2_BIT (1<<20)
+#define CONSTLEN_3_BIT (1<<21)
+#define HITACHI_BIT (1<<22)
/* Nonzero if we should generate code using type 0 insns */
#define TARGET_SH0 (target_flags & SH0_BIT)
@@ -88,28 +112,81 @@ extern int target_flags;
/* Nonzero to align doubles on 64 bit boundaries */
#define TARGET_ALIGN_DOUBLE (target_flags & DALIGN_BIT)
+/* Nonzero to use long jump tables */
+#define TARGET_BIGTABLE (target_flags & BIGTABLE_BIT)
-/* Nonzero if Combine dumping wanted */
+/* Nonzero if combine dumping wanted */
#define TARGET_CDUMP (target_flags & C_BIT)
-#define TARGET_SWITCHES \
-{ {"isize", ( ISIZE_BIT) },\
- {"space", ( SPACE_BIT) },\
- {"0", ( SH0_BIT) },\
- {"1", ( SH1_BIT) },\
- {"2", ( SH2_BIT) },\
- {"3", ( SH3_BIT) },\
- {"ac", ( MAC_BIT) },\
- {"dalign", ( DALIGN_BIT) },\
- {"c", ( C_BIT) },\
- {"r", ( RTL_BIT) },\
- {"R", ( R_BIT) },\
- {"", TARGET_DEFAULT} \
+/* Nonzero if trying to use reg+disp for QIs and HIs. This
+ doesn't work yet.*/
+#define TARGET_TRYR0 (target_flags & TRYR0_BIT)
+
+/* Nonzero if using no save calling convention */
+#define TARGET_NOSAVE (target_flags & NOSAVE_BIT)
+
+/* Nonzero if using no save calling convention */
+#define TARGET_SMALLCALL (target_flags & SMALLCALL_BIT)
+
+/* Select max size of computed constant code sequences to be 3 insns */
+#define TARGET_CLEN3 (target_flags & CONSTLEN_3_BIT)
+
+/* Nonzero if using Hitachi's calling convention */
+#define TARGET_HITACHI (target_flags & HITACHI_BIT)
+
+#define TARGET_SWITCHES \
+{ {"isize", ( ISIZE_BIT) }, \
+ {"space", ( SPACE_BIT) }, \
+ {"0", ( SH0_BIT) }, \
+ {"1", ( SH1_BIT) }, \
+ {"2", ( SH2_BIT) }, \
+ {"3", ( SH3_BIT) }, \
+ {"ac", ( MAC_BIT) }, \
+ {"dalign", ( DALIGN_BIT) }, \
+ {"c", ( C_BIT) }, \
+ {"r", ( RTL_BIT) }, \
+ {"bigtable", ( BIGTABLE_BIT)}, \
+ {"try-r0", ( TRYR0_BIT)}, \
+ {"R", ( R_BIT) }, \
+ {"nosave", ( NOSAVE_BIT) }, \
+ {"clen3", ( CONSTLEN_3_BIT) }, \
+ {"smallcall", ( SMALLCALL_BIT) }, \
+ {"hitachi", ( HITACHI_BIT) }, \
+ {"", TARGET_DEFAULT} \
}
-#define TARGET_DEFAULT FAST_BIT
-
-#define OVERRIDE_OPTIONS override_options();
+#define TARGET_DEFAULT (FAST_BIT)
+
+/* Macro to define table for command options with values. */
+#define TARGET_OPTIONS \
+ { { "maxsi-", &max_si}, \
+ { "maxhi-", &max_hi} }
+
+#define OVERRIDE_OPTIONS \
+do { \
+ sh_cpu = CPU_SH0; \
+ if (TARGET_SH1) \
+ sh_cpu = CPU_SH1; \
+ if (TARGET_SH2) \
+ sh_cpu = CPU_SH2; \
+ if (TARGET_SH3) \
+ sh_cpu = CPU_SH3; \
+ \
+ /* We *MUST* always define optimize since we *HAVE* to run \
+ shorten branches to get correct code. */ \
+ \
+ optimize = 1; \
+ flag_delayed_branch = 1; \
+ \
+ if (max_si) \
+ max_count_si = atoi (max_si); \
+ else \
+ max_count_si = 1010; \
+ if (max_hi) \
+ max_count_hi = atoi (max_hi); \
+ else \
+ max_count_hi = 505; \
+} while (0)
/* Target machine storage Layout. */
@@ -171,7 +248,7 @@ extern int target_flags;
#define CONSTANT_ALIGNMENT(EXP, ALIGN) \
((TREE_CODE (EXP) == STRING_CST \
&& (ALIGN) < FASTEST_ALIGNMENT) \
- ? FASTEST_ALIGNMENT : (ALIGN))
+ ? FASTEST_ALIGNMENT : (ALIGN))
/* Make arrays of chars word-aligned for the same reasons. */
#define DATA_ALIGNMENT(TYPE, ALIGN) \
@@ -186,13 +263,13 @@ extern int target_flags;
/* Standard register usage. */
-/* Register allocation for our first guess
+/* Register allocation for the Hitachi calling convention:
- r0-r3 scratch
- r4-r7 args in and out
- r8-r12 call saved
- r13 assembler temp
- r14 frame pointer
+ r0 arg return
+ r1..r3 scratch
+ r4-r7 args in
+ r8..r13 call saved
+ r14 frame pointer/call saved
r15 stack pointer
ap arg pointer (doesn't really exist, always eliminated)
pr subroutine return address
@@ -207,23 +284,33 @@ extern int target_flags;
All registers that the compiler knows about must be given numbers,
even those that are not normally considered general registers.
- SH has 16 integer registers and 4 control registers + the arg
- pointer */
-
-#define FIRST_PSEUDO_REGISTER 22
+*/
+#define AP_REG 16
#define PR_REG 17
#define T_REG 18
#define GBR_REG 19
#define MACH_REG 20
#define MACL_REG 21
+#define FIRST_PSEUDO_REGISTER 22
/* 1 for registers that have pervasive standard uses
and are not available for the register allocator. */
- /* r0 r1 r2 r3 r4 r5 r6 r7 r8 r9 r10 r11 r12 r13 r14 r15 ap pr t gbr mh ml */
-#define FIXED_REGISTERS \
- { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1}
+ /* r0 r1 r2 r3
+ r4 r5 r6 r7
+ r8 r9 r10 r11
+ r12 r13 r14 r15
+ ap pr t gbr
+ mh ml */
+
+#define FIXED_REGISTERS \
+ { 0, 0, 0, 0, \
+ 0, 0, 0, 0, \
+ 0, 0, 0, 0, \
+ 0, 0, 0, 1, \
+ 1, 1, 1, 1, \
+ 1, 1}
/* 1 for registers not available across function calls.
These must include the FIXED_REGISTERS and also any
@@ -232,9 +319,20 @@ extern int target_flags;
and the register where structure-value addresses are passed.
Aside from that, you can include as many other registers as you like. */
- /* r0 r1 r2 r3 r4 r5 r6 r7 r8 r9 r10 r11 r12 r13 r14 r15 ap pr t gbr mh ml */
-#define CALL_USED_REGISTERS \
- { 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1}
+ /* r0 r1 r2 r3
+ r4 r5 r6 r7
+ r8 r9 r10 r11
+ r12 r13 r14 r15
+ ap pr t gbr
+ mh ml */
+
+#define CALL_USED_REGISTERS \
+ { 1, 1, 1, 1, \
+ 1, 1, 1, 1, \
+ 0, 0, 0, 0, \
+ 0, 0, 0, 1, \
+ 1, 0, 1, 1, \
+ 1, 1}
/* Return number of consecutive hard regs needed starting at reg REGNO
to hold something of mode MODE.
@@ -242,14 +340,16 @@ extern int target_flags;
but can be less for certain modes in special long registers.
On the SH regs are UNITS_PER_WORD bits wide; */
+
#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.
We may keep double values in even registers */
+extern int hard_regno_mode_ok[];
#define HARD_REGNO_MODE_OK(REGNO, MODE) \
- ((TARGET_ALIGN_DOUBLE && GET_MODE_SIZE(MODE) > 4) ? (((REGNO)&1)==0) : 1)
+ (hard_regno_mode_ok[REGNO] & (1<<(int)MODE))
/* Value is 1 if it is a good idea to tie two pseudo registers
when one has mode MODE1 and one has mode MODE2.
@@ -275,8 +375,7 @@ extern int target_flags;
Zero means the frame pointer need not be set up (and parms may be accessed
via the stack pointer) in functions that seem suitable. */
-
-#define FRAME_POINTER_REQUIRED (get_frame_size() > 1000)
+#define FRAME_POINTER_REQUIRED 0
/* Definitions for register eliminations.
@@ -290,11 +389,17 @@ extern int target_flags;
followed by "to". Eliminations of the same "from" register are listed
in order of preference. */
+/* This is an array of structures. Each structure initializes one pair
+ of eliminable registers. The "from" register number is given first,
+ followed by "to". Eliminations of the same "from" register are listed
+ in order of preference. */
+
#define ELIMINABLE_REGS \
{{ FRAME_POINTER_REGNUM, STACK_POINTER_REGNUM}, \
{ ARG_POINTER_REGNUM, STACK_POINTER_REGNUM}, \
{ ARG_POINTER_REGNUM, FRAME_POINTER_REGNUM},}
+
/* Given FROM and TO register numbers, say whether this elimination
is allowed. */
#define CAN_ELIMINATE(FROM, TO) \
@@ -314,9 +419,21 @@ extern int target_flags;
/* The register in which a struct value address is passed */
-#define STRUCT_VALUE_REGNUM 3
+#define STRUCT_VALUE_REGNUM 2
+
+/* If the structure value address is not passed in a register, define
+ `STRUCT_VALUE' as an expression returning an RTX for the place
+ where the address is passed. If it returns 0, the address is
+ passed as an "invisible" first argument. */
+
+/*#define STRUCT_VALUE ((rtx)0)*/
+/* Don't default to pcc-struct-return, because we have already specified
+ exactly how to return structures in the RETURN_IN_MEMORY macro. */
+
+#define DEFAULT_PCC_STRUCT_RETURN 0
+
/* Define the classes of registers for register constraints in the
machine description. Also define ranges of constants.
@@ -347,10 +464,10 @@ enum reg_class
{
NO_REGS,
R0_REGS,
- GENERAL_REGS,
PR_REGS,
T_REGS,
MAC_REGS,
+ GENERAL_REGS,
ALL_REGS,
LIM_REG_CLASSES
};
@@ -362,10 +479,10 @@ enum reg_class
{ \
"NO_REGS", \
"R0_REGS", \
- "GENERAL_REGS", \
"PR_REGS", \
"T_REGS", \
"MAC_REGS", \
+ "GENERAL_REGS", \
"ALL_REGS", \
}
@@ -377,10 +494,10 @@ enum reg_class
{ \
0x000000, /* NO_REGS */ \
0x000001, /* R0_REGS */ \
- 0x01FFFF, /* GENERAL_REGS */ \
0x020000, /* PR_REGS */ \
0x040000, /* T_REGS */ \
0x300000, /* MAC_REGS */ \
+ 0x01FFFF, /* GENERAL_REGS */ \
0x37FFFF /* ALL_REGS */ \
}
@@ -392,9 +509,15 @@ enum reg_class
extern int regno_reg_class[];
#define REGNO_REG_CLASS(REGNO) regno_reg_class[REGNO]
+/* When defined, the compiler allows registers explicitly used in the
+ rtl to be used as spill registers but prevents the compiler from
+ extending the lifetime of these registers. */
+
+#define SMALL_REGISTER_CLASSES
+
/* The order in which register should be allocated. */
#define REG_ALLOC_ORDER \
- { 1,2,3,7,4,5,6,0,8,9,10,11,12,13,14,15,16,17,18,19,20,21}
+ { 1,2,3,7,6,5,4,0,8,9,10,11,12,13,14,15,16,17,18,19,20,21 }
/* The class value for index registers, and the one for base regs. */
#define INDEX_REG_CLASS R0_REGS
@@ -415,20 +538,22 @@ extern enum reg_class reg_class_from_letter[];
Return 1 if VALUE is in the range specified by C.
I: arithmetic operand -127..128, as used in add, sub, etc
L: logical operand 0..255, as used in and, or, etc.
+ J: something ok as a move source - so it must be easy to make
M: constant 1
+ N: constant 0
K: shift operand 1,2,8 or 16 */
#define CONST_OK_FOR_I(VALUE) (((int)(VALUE))>= -128 && ((int)(VALUE)) <= 127)
#define CONST_OK_FOR_L(VALUE) (((int)(VALUE))>= 0 && ((int)(VALUE)) <= 255)
#define CONST_OK_FOR_M(VALUE) ((VALUE)==1)
+#define CONST_OK_FOR_N(VALUE) ((VALUE)==0)
#define CONST_OK_FOR_K(VALUE) ((VALUE)==1||(VALUE)==2||(VALUE)==8||(VALUE)==16)
-
#define CONST_OK_FOR_LETTER_P(VALUE, C) \
((C) == 'I' ? CONST_OK_FOR_I (VALUE) \
+ : (C) == 'K' ? CONST_OK_FOR_K (VALUE) \
: (C) == 'L' ? CONST_OK_FOR_L (VALUE) \
: (C) == 'M' ? CONST_OK_FOR_M (VALUE) \
- : (C) == 'K' ? CONST_OK_FOR_K (VALUE) \
: 0)
/* Similar, but for floating constants, and defining letters G and H.
@@ -444,13 +569,12 @@ extern enum reg_class reg_class_from_letter[];
In general this is just CLASS; but on some machines
in some cases it is preferable to use a more restrictive class. */
-#define PREFERRED_RELOAD_CLASS(X, CLASS) CLASS
+#define PREFERRED_RELOAD_CLASS(X, CLASS) CLASS
/* Return the register class of a scratch register needed to copy IN into
or out of a register in CLASS in MODE. If it can be done directly,
NO_REGS is returned. */
-#define SECONDARY_RELOAD_CLASS(CLASS, MODE, X) NO_REGS
/* Return the maximum number of consecutive registers
needed to represent mode MODE in a register of class CLASS.
@@ -459,6 +583,7 @@ extern enum reg_class reg_class_from_letter[];
#define CLASS_MAX_NREGS(CLASS, MODE) \
((GET_MODE_SIZE (MODE) + UNITS_PER_WORD - 1) / UNITS_PER_WORD)
+
/* Stack layout; function entry, exit and calling. */
@@ -466,22 +591,21 @@ extern enum reg_class reg_class_from_letter[];
These two macros are used only in other macro definitions below. */
#define NPARM_REGS 4
#define FIRST_PARM_REG 4
-#define FIRST_RET_REG 4
+#define FIRST_RET_REG 0
/* Define this if pushing a word on the stack
makes the stack pointer a smaller address. */
#define STACK_GROWS_DOWNWARD
-/* Define this 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
+/* Define this macro if the addresses of local variable slots are at
+ negative offsets from the frame pointer.
-/* 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. */
+ The SH only has positive indexes, so grow the frame up
+*/
+/* #define FRAME_GROWS_DOWNWARD */
+
+/* Offset from the frame pointer to the first local variable slot to
+ be allocated. */
#define STARTING_FRAME_OFFSET 0
/* If we generate an insn to push BYTES bytes,
@@ -505,8 +629,11 @@ extern enum reg_class reg_class_from_letter[];
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) \
- gen_rtx (REG, TYPE_MODE (VALTYPE), FIRST_RET_REG)
+
+#define FUNCTION_VALUE(VALTYPE, FUNC) \
+ gen_rtx (REG, \
+ TYPE_MODE (VALTYPE) == BLKmode ? SImode : TYPE_MODE (VALTYPE), \
+ FIRST_RET_REG)
/* Define how to find the value returned by a library function
assuming the value has mode MODE. */
@@ -514,7 +641,7 @@ extern enum reg_class reg_class_from_letter[];
gen_rtx (REG, MODE, FIRST_RET_REG)
/* 1 if N is a possible register number for a function value.
- On the SH, only r4 can return results. */
+ On the SH, only r0 can return results. */
#define FUNCTION_VALUE_REGNO_P(REGNO) \
((REGNO) == FIRST_RET_REG)
@@ -593,6 +720,7 @@ extern enum reg_class reg_class_from_letter[];
#define FUNCTION_ARG(CUM, MODE, TYPE, NAMED) \
(NAMED && ROUND_REG ((CUM), (MODE)) < NPARM_REGS \
+ && (MODE) != BLKmode \
&& ((TYPE)==0 || ! TREE_ADDRESSABLE ((tree)(TYPE))) \
&& ((TYPE)==0 || (MODE) != BLKmode \
|| (TYPE_ALIGN ((TYPE)) % PARM_BOUNDARY == 0)) \
@@ -603,20 +731,10 @@ extern enum reg_class reg_class_from_letter[];
/* For an arg passed partly in registers and partly in memory,
this is the number of registers used.
For args passed entirely in registers or entirely in memory, zero.
- Any arg that starts in the first NPARM_REGS regs but won't entirely
- fit in them needs partial registers on the SH. */
-
-#define FUNCTION_ARG_PARTIAL_NREGS(CUM, MODE, TYPE, NAMED) \
- ((ROUND_REG ((CUM), (MODE)) < NPARM_REGS \
- && ((TYPE)==0 || ! TREE_ADDRESSABLE ((tree)(TYPE))) \
- && ((TYPE)==0 || (MODE) != BLKmode \
- || (TYPE_ALIGN ((TYPE)) % PARM_BOUNDARY == 0)) \
- && (ROUND_REG ((CUM), (MODE)) \
- + ((MODE) == BLKmode \
- ? ROUND_ADVANCE (int_size_in_bytes (TYPE)) \
- : ROUND_ADVANCE (GET_MODE_SIZE (MODE)))) - NPARM_REGS > 0) \
- ? (NPARM_REGS - ROUND_REG ((CUM), (MODE))) \
- : 0)
+
+ We never split args */
+
+#define FUNCTION_ARG_PARTIAL_NREGS(CUM, MODE, TYPE, NAMED) 0
extern int current_function_anonymous_args;
@@ -647,8 +765,8 @@ extern int current_function_anonymous_args;
/* Generate the assembly code for function exit
Just dump out any accumulated constant table.*/
-#define FUNCTION_EPILOGUE(STREAM, SIZE) \
- dump_constants(0);
+#define FUNCTION_EPILOGUE(STREAM, SIZE) function_epilogue (STREAM, SIZE)
+
/* Output assembler code for a block containing the constant parts
of a trampoline, leaving space for the variable parts.
@@ -691,7 +809,6 @@ extern int current_function_anonymous_args;
/* Addressing modes, and classification of registers for them. */
-
/*#define HAVE_POST_INCREMENT 1*/
/*#define HAVE_PRE_INCREMENT 1*/
/*#define HAVE_POST_DECREMENT 1*/
@@ -706,27 +823,27 @@ extern int current_function_anonymous_args;
has been allocated, which happens in local-alloc.c.
*/
+
#define REGNO_OK_FOR_BASE_P(REGNO) \
((REGNO) < PR_REG || (unsigned) reg_renumber[(REGNO)] < PR_REG)
-
-#define REGNO_OK_FOR_INDEX_P(REGNO) ((REGNO)==0)
+#define REGNO_OK_FOR_INDEX_P(REGNO) \
+ ((REGNO) == 0 || (unsigned) reg_renumber[(REGNO)] == 0)
/* Maximum number of registers that can appear in a valid memory
address. */
-#define MAX_REGS_PER_ADDRESS 4
+#define MAX_REGS_PER_ADDRESS 2
/* Recognize any constant value that is a valid address. */
#define CONSTANT_ADDRESS_P(X) \
(GET_CODE (X) == LABEL_REF)
-/* Nonzero if the constant value X is a legitimate general operand.
- It is given that X satisfies CONSTANT_P or is a CONST_DOUBLE.
+/* Nonzero if the constant value X is a legitimate general operand. */
- On the SH, allow anything but a double */
+#define LEGITIMATE_CONSTANT_P(X) \
+ (GET_CODE(X) != CONST_DOUBLE && GET_CODE(X) != LABEL_REF)
-#define LEGITIMATE_CONSTANT_P(X) (GET_CODE(X) != CONST_DOUBLE)
/* The macros REG_OK_FOR..._P assume that the arg is a REG rtx
and check its validity for a certain class.
@@ -735,23 +852,27 @@ extern int current_function_anonymous_args;
them unless they have been allocated suitable hard regs.
The symbol REG_OK_STRICT causes the latter definition to be used. */
+#define MODE_DISP_OK_4(X,MODE) ((GET_MODE_SIZE(MODE)==4) && ((unsigned)INTVAL(X)<64))
+#define MODE_DISP_OK_2(X,MODE) ((GET_MODE_SIZE(MODE)==2) && ((unsigned)INTVAL(X)<32) && TARGET_TRYR0)
+#define MODE_DISP_OK_1(X,MODE) ((GET_MODE_SIZE(MODE)==1) && ((unsigned)INTVAL(X)<16) && TARGET_TRYR0)
+
#ifndef REG_OK_STRICT
+
/* Nonzero if X is a hard reg that can be used as a base reg
or if it is a pseudo reg. */
#define REG_OK_FOR_BASE_P(X) \
- (REGNO (X) <= 16 || REGNO (X) >= FIRST_PSEUDO_REGISTER)
-
+ (REGNO (X) <= 16 || REGNO(X) >= FIRST_PSEUDO_REGISTER)
/* Nonzero if X is a hard reg that can be used as an index
or if it is a pseudo reg. */
+
#define REG_OK_FOR_INDEX_P(X) \
- (REGNO (X) == 0 || REGNO (X) >= FIRST_PSEUDO_REGISTER)
+ (REGNO (X) == 0 || REGNO(X) >= FIRST_PSEUDO_REGISTER)
#define REG_OK_FOR_PRE_POST_P(X) \
(REGNO (X) <= 16)
#else
-
/* Nonzero if X is a hard reg that can be used as a base reg. */
#define REG_OK_FOR_BASE_P(X) \
REGNO_OK_FOR_BASE_P (REGNO (X))
@@ -761,8 +882,29 @@ extern int current_function_anonymous_args;
REGNO_OK_FOR_INDEX_P (REGNO (X))
#define REG_OK_FOR_PRE_POST_P(X) \
- (REGNO (X) <= 16 || (unsigned) reg_renumber[REGNO (X)] <=16)
+ (REGNO (X) <= 16)
#endif
+
+/* The Q is a pc relative load operand */
+#define EXTRA_CONSTRAINT_Q(OP) \
+ (GET_CODE (OP) == MEM && GET_CODE (XEXP (OP,0)) == LABEL_REF)
+
+/* The U is a label ref */
+#define EXTRA_CONSTRAINT_U(OP) \
+ (GET_CODE (OP) == LABEL_REF)
+
+#define IS_INDEX(OP) \
+ ((GET_CODE(OP) == PLUS && \
+ (INDEX_REGISTER_RTX_P(XEXP(OP,0)) && BASE_REGISTER_RTX_P(XEXP(OP,1))) || \
+ (INDEX_REGISTER_RTX_P(XEXP(OP,1)) && BASE_REGISTER_RTX_P(XEXP(OP,0)))))
+
+
+
+#define EXTRA_CONSTRAINT(OP, C) \
+ ((C) == 'Q' ? EXTRA_CONSTRAINT_Q (OP) \
+ : (C) == 'U' ? EXTRA_CONSTRAINT_U (OP) \
+ : 0)
+
/* GO_IF_LEGITIMATE_ADDRESS recognizes an RTL expression
that is a valid memory address for an instruction.
@@ -796,18 +938,14 @@ extern int current_function_anonymous_args;
A legitimate index for a QI or HI is 0, SI and above can be any
number 0..63 */
-#define GO_IF_LEGITIMATE_INDEX(MODE, REGNO, OP, LABEL) \
- do { \
- if (GET_CODE (OP) == CONST_INT) \
- { \
- if (0&&GET_MODE_SIZE (MODE) == 2 && ((unsigned)INTVAL(OP)) <=30)\
- goto LABEL; \
- if (0&&GET_MODE_SIZE (MODE) == 1 && ((unsigned)INTVAL(OP)) <=15)\
- goto LABEL; \
- if (GET_MODE_SIZE (MODE) >=4 \
- && ((unsigned)INTVAL(OP)) < 64) \
- goto LABEL; \
- } \
+#define GO_IF_LEGITIMATE_INDEX(MODE, REGNO, OP, LABEL) \
+ do { \
+ if (GET_CODE (OP) == CONST_INT) \
+ { \
+ if (MODE_DISP_OK_4 (OP, MODE)) goto LABEL; \
+ if (MODE_DISP_OK_2 (OP, MODE)) goto LABEL; \
+ if (MODE_DISP_OK_1 (OP, MODE)) goto LABEL; \
+ } \
} while(0)
@@ -819,13 +957,13 @@ extern int current_function_anonymous_args;
&& GET_CODE (XEXP (X, 0)) == REG \
&& REG_OK_FOR_PRE_POST_P (XEXP (X, 0))) \
goto LABEL; \
- else if (GET_CODE (X) == PLUS || GET_CODE(X) == LO_SUM) \
+ else if (GET_CODE (X) == PLUS) \
{ \
rtx xop0 = XEXP(X,0); \
rtx xop1 = XEXP(X,1); \
- if (GET_MODE_SIZE(MODE) >= 4 && BASE_REGISTER_RTX_P (xop0)) \
+ if (GET_MODE_SIZE(MODE) <= 4 && BASE_REGISTER_RTX_P (xop0)) \
GO_IF_LEGITIMATE_INDEX (MODE, REGNO (xop0), xop1, LABEL); \
- if (GET_MODE_SIZE(MODE) >= 4 && BASE_REGISTER_RTX_P (xop1)) \
+ if (GET_MODE_SIZE(MODE) <= 4 && BASE_REGISTER_RTX_P (xop1)) \
GO_IF_LEGITIMATE_INDEX (MODE, REGNO (xop1), xop0, LABEL); \
if (GET_MODE_SIZE(MODE)<=4) { \
if(BASE_REGISTER_RTX_P(xop1) && \
@@ -869,12 +1007,12 @@ extern int current_function_anonymous_args;
/* Specify the machine mode that this machine uses
for the index in the tablejump instruction. */
-#define CASE_VECTOR_MODE SImode
+#define CASE_VECTOR_MODE (TARGET_BIGTABLE ? SImode : HImode)
/* Define this if the tablejump instruction expects the table
to contain offsets from the address of the table.
Do not define this if the table should contain absolute addresses. */
-/* #define CASE_VECTOR_PC_RELATIVE */
+#define CASE_VECTOR_PC_RELATIVE
/* Specify the tree operation to be used to convert reals to integers. */
#define IMPLICIT_FIX_EXPR FIX_ROUND_EXPR
@@ -889,7 +1027,7 @@ extern int current_function_anonymous_args;
#define SIZE_TYPE "unsigned int"
/* Don't cse the address of the function being compiled. */
-#define NO_RECURSIVE_FUNCTION_CSE 1
+/*#define NO_RECURSIVE_FUNCTION_CSE 1*/
/* Max number of bytes we can move from memory to memory
in one reasonably fast instruction. */
@@ -940,15 +1078,12 @@ extern int current_function_anonymous_args;
#define Pmode SImode
#define FUNCTION_MODE Pmode
-/* The structure type of the machine dependent info field of insns
- No uses for this yet. */
-/* #define INSN_MACHINE_INFO struct machine_info */
-
/* The relative costs of various types of constants. Note that cse.c defines
REG = 1, SUBREG = 2, any node = (2 + sum of subnodes). */
#define CONST_COSTS(RTX, CODE, OUTER_CODE) \
case CONST_INT: \
+ if (INTVAL(RTX)==0) return 0; \
if (CONST_OK_FOR_I (INTVAL(RTX))) \
return 1; \
else \
@@ -961,6 +1096,8 @@ extern int current_function_anonymous_args;
return 10;
#define RTX_COSTS(X, CODE, OUTER_CODE) \
+ case AND: \
+ return COSTS_N_INSNS (andcosts (X)); \
case MULT: \
return COSTS_N_INSNS (multcosts (X)); \
case ASHIFT: \
@@ -976,6 +1113,34 @@ extern int current_function_anonymous_args;
case FIX: \
return 100;
+
+/* The multiply and divide insns on the SH are actually function calls
+ with some special constraints on arguments and register usage.
+
+ These macros tell reorg that the references to arguments and
+ register clobbers for insns of type sfunc do not appear to happen
+ until after the millicode call. This allows reorg to put insns
+ which set the argument registers into the delay slot of the millicode
+ call -- thus they act more like traditional CALL_INSNs.
+
+ get_attr_type will try to recognize the given insn, so make sure to
+ filter out things it will not accept -- SEQUENCE, USE and CLOBBER insns
+ in particular. */
+
+#define INSN_SETS_ARE_DELAYED(X) \
+ ((GET_CODE (X) == INSN \
+ && GET_CODE (PATTERN (X)) != SEQUENCE \
+ && GET_CODE (PATTERN (X)) != USE \
+ && GET_CODE (PATTERN (X)) != CLOBBER \
+ && get_attr_type (X) == TYPE_SFUNC))
+
+#define INSN_REFERENCES_ARE_DELAYED(X) \
+ ((GET_CODE (X) == INSN \
+ && GET_CODE (PATTERN (X)) != SEQUENCE \
+ && GET_CODE (PATTERN (X)) != USE \
+ && GET_CODE (PATTERN (X)) != CLOBBER \
+ && get_attr_type (X) == TYPE_SFUNC))
+
/* Compute extra cost of moving data between one register class
and another.
@@ -984,7 +1149,7 @@ extern int current_function_anonymous_args;
*/
#define REGISTER_MOVE_COST(SRCCLASS, DSTCLASS) \
- ((DSTCLASS == T_REGS) ? 10 : 1)
+ (((DSTCLASS == T_REGS) || (DSTCLASS == PR_REG)) ? 10 : 1)
/* Assembler output control */
@@ -994,23 +1159,23 @@ extern int current_function_anonymous_args;
W_options, sizeof W_options / sizeof W_options[0]);
-#define ASM_FILE_END(STREAM) \
- dump_constants(0);
+#define ASM_FILE_END(STREAM)
-#define ASM_APP_ON ""
-#define ASM_APP_OFF ""
-#define FILE_ASM_OP "\t.file\n"
-#define IDENT_ASM_OP "\t.ident\n"
+#define ASM_APP_ON ""
+#define ASM_APP_OFF ""
+#define FILE_ASM_OP "\t.file\n"
+#define IDENT_ASM_OP "\t.ident\n"
+/* How to change between sections. */
-/* Switch to the text or data segment. */
-#define TEXT_SECTION_ASM_OP "\t.text"
-#define DATA_SECTION_ASM_OP "\t.data"
-#define CTORS_SECTION_ASM_OP "\t.section\t.ctors\n"
-#define DTORS_SECTION_ASM_OP "\t.section\t.dtors\n"
+#define TEXT_SECTION_ASM_OP "\t.text"
+#define DATA_SECTION_ASM_OP "\t.data"
+#define CTORS_SECTION_ASM_OP "\t.section\t.ctors\n"
+#define DTORS_SECTION_ASM_OP "\t.section\t.dtors\n"
+#define INIT_SECTION_ASM_OP "\t.section\t.init\n"
+#define EXTRA_SECTIONS in_ctors, in_dtors
-#define EXTRA_SECTIONS in_ctors, in_dtors
#define EXTRA_SECTION_FUNCTIONS \
void \
ctors_section() \
@@ -1040,8 +1205,8 @@ dtors_section() \
#define ASM_OUTPUT_DESTRUCTOR(FILE,NAME) \
do { dtors_section(); fprintf(FILE,"\t.long\t_%s\n", NAME); } while (0)
-
#undef DO_GLOBAL_CTORS_BODY
+
#define DO_GLOBAL_CTORS_BODY \
{ \
typedef (*pfunc)(); \
@@ -1068,14 +1233,12 @@ dtors_section() \
}
-
#define ASM_OUTPUT_REG_PUSH(file, v) \
fprintf (file, "\tmov.l r%s,-@r15\n", v);
#define ASM_OUTPUT_REG_POP(file, v) \
fprintf (file, "\tmov.l @r15+,r%s\n", v);
-
/* The assembler's names for the registers. RFP need not always be used as
the Real framepointer; it can also be used as a normal general register.
@@ -1133,18 +1296,25 @@ dtors_section() \
((OUTVAR) = (char *) alloca (strlen (NAME) + 10), \
sprintf ((OUTVAR), "%s.%d", (NAME), (NUMBER)))
-/* Jump tables must be 32 bit aligned. */
+/* Jump tables must be 32 bit aligned, no matter the size of the element */
#define ASM_OUTPUT_CASE_LABEL(STREAM,PREFIX,NUM,TABLE) \
- fprintf (STREAM, "\t.align 2\n%s%d:\n", PREFIX, NUM);
+ fprintf (STREAM, "\t.align 2\n%s%d:\n", PREFIX, NUM);
+
+/* Output a relative address table. */
-/* Output a relative address. Not needed since jump tables are absolute
- but we must define it anyway. */
-#define ASM_OUTPUT_ADDR_DIFF_ELT(STREAM,VALUE,REL) \
- fputs ("- - - ASM_OUTPUT_ADDR_DIFF_ELT called!\n", STREAM)
+#define ASM_OUTPUT_ADDR_DIFF_ELT(STREAM,VALUE,REL) \
+ if (TARGET_BIGTABLE) \
+ fprintf (STREAM, "\t.long L%d-L%d\n", VALUE,REL); \
+ else \
+ fprintf (STREAM, "\t.word L%d-L%d\n", VALUE,REL); \
-/* Output an element of a dispatch table. */
-#define ASM_OUTPUT_ADDR_VEC_ELT(STREAM,VALUE) \
- fprintf (STREAM, "\t.long\tL%d\n", VALUE)
+/* Output an absolute table element */
+
+#define ASM_OUTPUT_ADDR_VEC_ELT(STREAM,VALUE) \
+ if (TARGET_BIGTABLE) \
+ fprintf (STREAM, "\t.long L%d\n", VALUE); \
+ else \
+ fprintf (STREAM, "\t.word L%d\n", VALUE); \
/* Output various types of constants. */
@@ -1165,7 +1335,6 @@ do { char dstr[30]; \
fprintf (FILE, "\t.float %s\n", dstr); \
} while (0)
-
#define ASM_OUTPUT_INT(STREAM, EXP) \
(fprintf (STREAM, "\t.long\t"), \
output_addr_const (STREAM, (EXP)), \
@@ -1237,16 +1406,13 @@ do { char dstr[30]; \
#define PRINT_OPERAND_ADDRESS(STREAM,X) print_operand_address (STREAM, X)
#define PRINT_OPERAND_PUNCT_VALID_P(CHAR) \
- ((CHAR)=='.' || (CHAR) == '#' || (CHAR) == '*' || (CHAR) == '^' || (CHAR) == '!')
+ ((CHAR)=='.' || (CHAR) == '#' || (CHAR) == '*' || (CHAR) == '^' || (CHAR)=='!')
-/* Define the information needed to generate branch insns. This is stored
- from the compare operation. Note that we can't use "rtx" here since it
- hasn't been defined! */
-
extern struct rtx_def *sh_compare_op0;
extern struct rtx_def *sh_compare_op1;
extern struct rtx_def *prepare_scc_operands();
+extern struct rtx_def *table_lab;
extern enum attr_cpu sh_cpu; /* target cpu */
@@ -1256,12 +1422,24 @@ extern char *output_branch();
extern char *output_shift();
extern char *output_movedouble();
extern char *output_movepcrel();
+extern char *output_jump_label_table();
+extern char *output_far_jump();
+
+#define MACHINE_DEPENDENT_REORG(X) machine_dependent_reorg(X)
-#define ADJUST_INSN_LENGTH(insn, length) \
- adjust_insn_length (insn, insn_lengths)
+/* Generate calls to memcpy, memcmp and memset. */
+#define TARGET_MEM_FUNCTIONS
+#define HANDLE_PRAGMA(finput) handle_pragma (finput)
+/* Set when processing a function with pragma interrupt turned on. */
+extern int pragma_interrupt;
+#define MOVE_RATIO 16
+char *max_si;
+char *max_hi;
+int max_count_si;
+int max_count_hi;
diff --git a/gcc/config/sh/sh.md b/gcc/config/sh/sh.md
index 5ba2573..3bdb801 100644
--- a/gcc/config/sh/sh.md
+++ b/gcc/config/sh/sh.md
@@ -18,10 +18,37 @@
;; along with GNU CC; see the file COPYING. If not, write to
;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
-;;- See file "rtl.def" for documentation on define_insn, match_*, et. al.
-
+;; Special constraints for SH machine description:
+;;
+;; t -- T
+;; x -- mac
+;; l -- pr
+;; z -- r0
+;;
+;; Special formats used for outputting SH instructions:
+;;
+;; %. -- print a .s if insn needs delay slot
+;; %* -- print a local label
+;; %^ -- increment the local label number
+;; %# -- output a nop if there is nothing to put in the delay slot
+;; %R -- print the next register or memory location along, ie the lsw in
+;; a double word value
+;; %O -- print a constant without the #
+;; %M -- print a constant as its negative
+;;
+;;
+;; Special predicates:
+;;
+;; arith_operand -- operand is valid source for arithmetic op
+;; arith_reg_operand -- operand is valid register for arithmetic op
+;; byte_index_operand -- operand is ok as an index in a mov.b
+;; general_movdst_operand -- operand is valid move destination
+;; general_movsrc_operand -- operand is valid move source
+;; logical_operand -- operand is valid source for logical op
+;; pop_operand -- operand is a pop from the stack
+;; system_reg_operand -- operand is MACL, MACH, or PR
;; -------------------------------------------------------------------------
;; Attributes
;; -------------------------------------------------------------------------
@@ -30,66 +57,98 @@
(define_attr "cpu" "sh0,sh1,sh2,sh3"
(const (symbol_ref "sh_cpu")))
-
-(define_attr "type" "cbranch,ctable,jump,arith,other,load,store,move,smpy,dmpy,return,pload"
+;;
+;; cbranch conditional branch instructions
+;; jump unconditional jumps
+;; arith ordinary arithmetic
+;; load from memory
+;; store to memory
+;; move register to register
+;; smpy single precision integer multiply
+;; dmpy double precision integer multiply
+;; return rts
+;; pload load of pr reg (can't be put into delay slot of rts)
+;; pcloadsi pc relative load of SI value
+;; pcloadhi pc relative load of HI value
+;; rte return from exception
+;; sfunc special function call with known used registers
+
+(define_attr "type"
+ "cbranch,jump,arith,other,load,store,move,smpy,dmpy,return,pload,pcloadsi,pcloadhi,rte,sfunc"
(const_string "other"))
-; If a conditional branch destination is within -100..100 bytes away
+; If a conditional branch destination is within -120..120 bytes away
; from the instruction it can be 2 bytes long. Something in the
-; range -4000..4000 bytes can be 6 bytes long, all other conditional
+; range -4090..4090 bytes can be 6 bytes long, all other conditional
; branches are 8 bytes long.
; An unconditional jump which can reach forward or back 4k can be
; 6 bytes long (including the delay slot). If it is too big, it
-; must be 8 bytes long.
+; must be 10 bytes long.
+; If a pcrel instruction is within 500 bytes of the constant, then the insn is
+; 2 bytes long, otherwise 12 bytes
; All other instructions are two bytes long by default.
(define_attr "length" ""
(cond [(eq_attr "type" "cbranch")
- (if_then_else (and (ge (minus (pc) (match_dup 0))
- (const_int -100))
- (le (minus (pc) (match_dup 0))
- (const_int 100)))
- (const_int 2)
- (if_then_else (and (ge (minus (pc) (match_dup 0))
- (const_int -4000))
- (le (minus (pc) (match_dup 0))
- (const_int 4000)))
- (const_int 6)
- (const_int 8)))
+ (if_then_else (and (ge (minus (pc) (match_dup 0))
+ (const_int -122))
+ (le (minus (pc) (match_dup 0))
+ (const_int 122)))
+ (const_int 2)
+ (if_then_else (and (ge (minus (pc) (match_dup 0))
+ (const_int -4090))
+ (le (minus (pc) (match_dup 0))
+ (const_int 4090)))
+ (const_int 6)
+ (const_int 16)))
(eq_attr "type" "jump")
- (if_then_else (and (ge (minus (pc) (match_dup 0))
- (const_int -4000))
- (le (minus (pc) (match_dup 0))
- (const_int 4000)))
- (const_int 4)
- (const_int 6))
+ (if_then_else (and (ge (minus (pc) (match_dup 0))
+ (const_int -4090))
+ (le (minus (pc) (match_dup 0))
+ (const_int 4090)))
+ (const_int 4)
+ (const_int 10))
+ (eq_attr "type" "pcloadsi")
+ (if_then_else (gt (pc) (minus (match_dup 0) (const_int 1000)))
+ (const_int 2)
+ (const_int 12))
+ (eq_attr "type" "pcloadhi")
+ (if_then_else (gt (pc) (minus (match_dup 0) (const_int 500)))
+ (const_int 2)
+ (const_int 12))
+
] (const_int 2)))
;; (define_function_unit {name} {num-units} {n-users} {test}
;; {ready-delay} {issue-delay} [{conflict-list}])
-(define_function_unit "memory" 1 1 (eq_attr "type" "load") 1 0)
-(define_function_unit "mpy" 1 1 (eq_attr "type" "smpy") 3 0)
-(define_function_unit "mpy" 1 1 (eq_attr "type" "dmpy") 5 0)
+(define_function_unit "memory" 1 0 (eq_attr "type" "load,pcloadsi,pcloadhi") 2 0)
+(define_function_unit "mpy" 1 0 (eq_attr "type" "smpy") 3 0)
+(define_function_unit "mpy" 1 0 (eq_attr "type" "dmpy") 5 0)
(define_attr "needs_delay_slot" "yes,no"
- (cond [(eq_attr "type" "jump") (const_string "yes")
+ (cond [(eq_attr "type" "jump") (const_string "yes")
(eq_attr "type" "return") (const_string "yes")]
(const_string "no")))
(define_delay
- (eq_attr "needs_delay_slot" "yes")
+ (eq_attr "needs_delay_slot" "yes")
[(eq_attr "in_delay_slot" "yes") (nil) (nil)])
+(define_delay
+ (eq_attr "type" "rte")
+ [(and (eq_attr "in_delay_slot" "yes")
+ (eq_attr "hit_stack" "no")) (nil) (nil)])
+
+(define_attr "hit_stack" "yes,no" (const_string "no"))
-(define_attr "dump" "yes,no,must" (const_string "no"))
-(define_attr "constneed" "yes,no" (const_string "no"))
-(define_attr "smallestsize" "" (const_int 2))
-(define_attr "largestsize" "" (const_int 8))
-(define_attr "constantsize" "" (const_int 4))
+(define_delay
+ (and (eq_attr "type" "cbranch")
+ (eq_attr "cpu" "sh2"))
+ [(eq_attr "in_delay_slot" "yes") (nil) (nil)])
(define_attr "in_delay_slot" "maybe,yes,no"
(cond [(eq_attr "type" "cbranch") (const_string "no")
@@ -100,7 +159,6 @@
(eq_attr "length" "4,6,8,10,12") (const_string "no")
] (const_string "yes")))
-
;; -------------------------------------------------------------------------
;; SImode signed integer comparisons
@@ -112,49 +170,50 @@
(const_int 1)))]
""
"movt %0 !movt1")
-
+
(define_insn ""
- [(set (reg:SI 18) (gt (match_operand:SI 0 "arith_reg_operand" "r")
- (const_int 0)))]
+ [(set (reg:SI 18) (gt:SI (match_operand:SI 0 "arith_reg_operand" "r")
+ (const_int 0)))]
""
"cmp/pl %0")
(define_insn ""
- [(set (reg:SI 18) (ge (match_operand:SI 0 "arith_reg_operand" "r")
- (const_int 0)))]
+ [(set (reg:SI 18) (ge:SI (match_operand:SI 0 "arith_reg_operand" "r")
+ (const_int 0)))]
""
"cmp/pz %0")
-(define_insn "cmpeqsi_t"
- [(set (reg:SI 18) (eq (match_operand:SI 0 "arith_reg_operand" "r,z")
- (match_operand:SI 1 "arith_operand" "r,I")))]
+(define_insn "cmpeq_0"
+ [(set (reg:SI 18) (eq:SI (match_operand:SI 0 "arith_reg_operand" "r")
+ (const_int 0)))]
""
- "cmp/eq %1,%0")
+ "tst %0,%0 ! t0")
+(define_insn "cmpeqsi_t"
+ [(set (reg:SI 18) (eq:SI (match_operand:SI 0 "arith_operand" "r,N,r,z")
+ (match_operand:SI 1 "arith_operand" "N,r,r,I")))]
+ ""
+ "@
+ tst %0,%0 !t1
+ tst %1,%1 !t2
+ cmp/eq %1,%0
+ cmp/eq %1,%0")
(define_insn "cmpgtsi_t"
- [(set (reg:SI 18) (gt (match_operand:SI 0 "arith_reg_operand" "r")
- (match_operand:SI 1 "arith_reg_operand" "r")))]
+ [(set (reg:SI 18) (gt:SI (match_operand:SI 0 "arith_reg_operand" "r,r")
+ (match_operand:SI 1 "arith_operand" "N,r")))]
""
- "cmp/gt %1,%0")
+ "@
+ cmp/pl %0
+ cmp/gt %1,%0")
(define_insn "cmpgesi_t"
- [(set (reg:SI 18) (ge (match_operand:SI 0 "arith_reg_operand" "r")
- (match_operand:SI 1 "arith_reg_operand" "r")))]
+ [(set (reg:SI 18) (ge:SI (match_operand:SI 0 "arith_reg_operand" "r,r")
+ (match_operand:SI 1 "arith_operand" "N,r")))]
""
- "cmp/ge %1,%0")
-
-(define_insn "cmpltsi_t"
- [(set (reg:SI 18) (lt (match_operand:SI 0 "arith_reg_operand" "r")
- (match_operand:SI 1 "arith_reg_operand" "r")))]
- ""
- "cmp/gt %0,%1")
-
-(define_insn "cmplesi_t"
- [(set (reg:SI 18) (le (match_operand:SI 0 "arith_reg_operand" "r")
- (match_operand:SI 1 "arith_reg_operand" "r")))]
- ""
- "cmp/ge %0,%1")
+ "@
+ cmp/pz %0
+ cmp/ge %1,%0")
;; -------------------------------------------------------------------------
@@ -162,38 +221,31 @@
;; -------------------------------------------------------------------------
(define_insn "cmpgeusi_t"
- [(set (reg:SI 18) (geu (match_operand:SI 0 "arith_reg_operand" "r")
- (match_operand:SI 1 "arith_reg_operand" "r")))]
+ [(set (reg:SI 18) (geu:SI (match_operand:SI 0 "arith_reg_operand" "r,r")
+ (match_operand:SI 1 "arith_operand" "N,r")))]
""
- "cmp/hs %1,%0")
+ "@
+ cmp/pz %1
+ cmp/hs %1,%0")
(define_insn "cmpgtusi_t"
- [(set (reg:SI 18) (gtu (match_operand:SI 0 "arith_reg_operand" "r")
- (match_operand:SI 1 "arith_reg_operand" "r")))]
- ""
- "cmp/hi %1,%0")
-
-(define_insn "cmpleusi_t"
- [(set (reg:SI 18) (leu (match_operand:SI 0 "arith_reg_operand" "r")
- (match_operand:SI 1 "arith_reg_operand" "r")))]
- ""
- "cmp/hs %0,%1")
-
-(define_insn "cmpltusi_t"
- [(set (reg:SI 18) (ltu (match_operand:SI 0 "arith_reg_operand" "r")
- (match_operand:SI 1 "arith_reg_operand" "r")))]
+ [(set (reg:SI 18) (gtu:SI (match_operand:SI 0 "arith_operand" "r,r")
+ (match_operand:SI 1 "arith_operand" "N,r")))]
""
- "cmp/hi %0,%1")
+ "@
+ cmp/pl %1
+ cmp/hi %1,%0")
;; We save the compare operands in the cmpxx patterns and use them when
;; we generate the branch.
(define_expand "cmpsi"
- [(set (reg:SI 18) (compare (match_operand:SI 0 "arith_reg_operand" "")
- (match_operand:SI 1 "arith_reg_operand" "")))]
+ [(set (reg:SI 18) (compare (match_operand:SI 0 "arith_operand" "")
+ (match_operand:SI 1 "arith_operand" "")))]
""
"
-{ sh_compare_op0 = operands[0];
+{
+ sh_compare_op0 = operands[0];
sh_compare_op1 = operands[1];
DONE;
}")
@@ -203,19 +255,36 @@
;; Addition instructions
;; -------------------------------------------------------------------------
-(define_insn "adddi3"
- [(set (match_operand:DI 0 "arith_reg_operand" "=&r")
- (plus:DI (match_operand:DI 1 "arith_reg_operand" "%0")
- (match_operand:DI 2 "arith_reg_operand" "r")))
+(define_insn "addc"
+ [(set (match_operand:SI 0 "arith_reg_operand" "=r")
+ (plus:SI (reg:SI 18)
+ (plus:SI (match_operand:SI 1 "arith_reg_operand" "%0")
+ (match_operand:SI 2 "arith_reg_operand" "r"))))
(clobber (reg:SI 18))]
""
- "clrt\;addc %R2,%R0\;addc %2,%0"
- [(set_attr "length" "6")
- (set_attr "in_delay_slot" "no")
- (set_attr "type" "arith")])
+ "addc %2,%0")
+
+;; this should be a define split.
+
+(define_expand "adddi3"
+ [(set (reg:SI 18) (const_int 0))
+ (parallel
+ [(set (subreg:SI (match_operand:DI 0 "arith_reg_operand" "=r") 1)
+ (plus:SI (reg:SI 18)
+ (plus:SI (subreg:SI (match_operand:DI 1 "arith_reg_operand" "r") 1)
+ (subreg:SI (match_operand:DI 2 "arith_reg_operand" "r") 1))))
+ (clobber (reg:SI 18))])
+ (parallel
+ [(set (subreg:SI (match_dup 0) 0)
+ (plus:SI (reg:SI 18)
+ (plus:SI (subreg:SI (match_dup 1) 0)
+ (subreg:SI (match_dup 2) 0))))
+ (clobber (reg:SI 18))])]
+ ""
+ "")
-(define_insn "addsi3_i"
+(define_insn "addsi3_real"
[(set (match_operand:SI 0 "arith_reg_operand" "=r")
(plus:SI (match_operand:SI 1 "arith_reg_operand" "%0")
(match_operand:SI 2 "arith_operand" "rI")))]
@@ -226,7 +295,7 @@
(define_expand "addsi3"
[(set (match_operand:SI 0 "arith_reg_operand" "=r")
- (plus:SI (match_operand:SI 1 "arith_reg_operand" "%0")
+ (plus:SI (match_operand:SI 1 "arith_operand" "%0")
(match_operand:SI 2 "arith_operand" "rI")))]
""
"")
@@ -237,7 +306,7 @@
;; -------------------------------------------------------------------------
(define_insn "subdi3"
- [(set (match_operand:DI 0 "arith_reg_operand" "=&r")
+ [(set (match_operand:DI 0 "arith_reg_operand" "=r")
(minus:DI (match_operand:DI 1 "arith_reg_operand" "0")
(match_operand:DI 2 "arith_reg_operand" "r")))
(clobber (reg:SI 18))]
@@ -248,27 +317,95 @@
(set_attr "type" "arith")])
(define_insn "subsi3"
- [(set (match_operand:SI 0 "arith_reg_operand" "=r,r")
- (minus:SI (match_operand:SI 1 "arith_reg_operand" "0,0")
- (match_operand:SI 2 "arith_operand" "r,I")))]
+ [(set (match_operand:SI 0 "arith_reg_operand" "=r")
+ (minus:SI (match_operand:SI 1 "arith_reg_operand" "0")
+ (match_operand:SI 2 "arith_operand" "r")))]
""
- "@
- sub %2,%0
- add %M2,%0"
+ "sub %2,%0"
[(set_attr "type" "arith")])
;; -------------------------------------------------------------------------
-;; Multiplication instructions
+;; Division instructions
;; -------------------------------------------------------------------------
+;; we take advantage of the library routines which don't clobber as many
+;; registers as a normal function call would.
+
+
+(define_insn ""
+ [(set (reg:SI 0)
+ (udiv:SI (reg:SI 4) (reg:SI 5)))
+ (clobber (reg:SI 18))
+ (clobber (reg:SI 17))
+ (clobber (reg:SI 6))
+ (clobber (reg:SI 4))
+ (use (match_operand:SI 0 "register_operand" "r"))]
+ ""
+ "jsr @%0%#"
+ [(set_attr "type" "sfunc")
+ (set_attr "length" "4")
+ (set_attr "needs_delay_slot" "yes")])
+
+(define_expand "udivsi3"
+ [(set (reg:SI 4) (match_operand:SI 1 "general_operand" "g"))
+ (set (reg:SI 5) (match_operand:SI 2 "general_operand" "g"))
+ (set (match_dup 3) (symbol_ref:SI "__udivsi3"))
+ (parallel[(set (reg:SI 0)
+ (udiv:SI (reg:SI 4)
+ (reg:SI 5)))
+ (clobber (reg:SI 18))
+ (clobber (reg:SI 17))
+ (clobber (reg:SI 6))
+ (clobber (reg:SI 4))
+ (use (match_dup 3))])
+ (set (match_operand:SI 0 "general_operand" "=g")
+ (reg:SI 0))]
+ ""
+ "operands[3] = gen_reg_rtx(SImode);")
+
+
+(define_insn ""
+ [(set (reg:SI 0)
+ (div:SI (reg:SI 4) (reg:SI 5)))
+ (clobber (reg:SI 18))
+ (clobber (reg:SI 17))
+ (clobber (reg:SI 4))
+ (clobber (reg:SI 3))
+ (use (match_operand:SI 0 "register_operand" "r"))]
+ ""
+ "jsr @%0%#"
+ [(set_attr "type" "sfunc")
+ (set_attr "length" "4")
+ (set_attr "needs_delay_slot" "yes")])
+
+(define_expand "divsi3"
+ [(set (reg:SI 4) (match_operand:SI 1 "general_operand" "g"))
+ (set (reg:SI 5) (match_operand:SI 2 "general_operand" "g"))
+ (set (match_dup 3) (symbol_ref:SI "__sdivsi3"))
+ (parallel[(set (reg:SI 0)
+ (div:SI (reg:SI 4)
+ (reg:SI 5)))
+ (clobber (reg:SI 18))
+ (clobber (reg:SI 17))
+ (clobber (reg:SI 4))
+ (clobber (reg:SI 3))
+ (use (match_dup 3))])
+ (set (match_operand:SI 0 "general_operand" "=g")
+ (reg:SI 0))]
+ ""
+ "operands[3] = gen_reg_rtx(SImode);")
+
+
+;; -------------------------------------------------------------------------
+;; Multiplication instructions
+;; -------------------------------------------------------------------------
+
(define_insn ""
[(set (reg:SI 21)
- (mult:SI (zero_extend:SI
- (match_operand:HI 1 "arith_reg_operand" "r"))
- (zero_extend:SI
- (match_operand:HI 2 "arith_reg_operand" "r"))))]
+ (mult:SI (zero_extend:SI (match_operand:HI 1 "arith_reg_operand" "r"))
+ (zero_extend:SI (match_operand:HI 2 "arith_reg_operand" "r"))))]
""
"mulu %2,%1"
[(set_attr "type" "smpy")])
@@ -305,12 +442,124 @@
""
"")
+;; mulsi3 on the SH2 can be done in one instruction, on the SH1 we generate
+;; a call to a routine which clobbers known registers.
+
+(define_insn ""
+ [(set (reg:SI 0)
+ (mult:SI (reg:SI 4) (reg:SI 5)))
+ (clobber (reg:SI 21))
+ (clobber (reg:SI 18))
+ (clobber (reg:SI 17))
+ (clobber (reg:SI 3))
+ (clobber (reg:SI 2))
+ (clobber (reg:SI 1))
+ (use (match_operand:SI 0 "register_operand" "r"))]
+ ""
+ "jsr @%0%#"
+ [(set_attr "type" "sfunc")
+ (set_attr "length" "4")
+ (set_attr "needs_delay_slot" "yes")])
+
+(define_expand "mulsi3_call"
+ [(set (reg:SI 4) (match_operand:SI 1 "general_operand" "g"))
+ (set (reg:SI 5) (match_operand:SI 2 "general_operand" "g"))
+ (set (match_dup 3) (symbol_ref:SI "__mulsi3"))
+ (parallel[(set (reg:SI 0)
+ (mult:SI (reg:SI 4)
+ (reg:SI 5)))
+ (clobber (reg:SI 21))
+ (clobber (reg:SI 18))
+ (clobber (reg:SI 17))
+ (clobber (reg:SI 3))
+ (clobber (reg:SI 2))
+ (clobber (reg:SI 1))
+ (use (match_dup 3))])
+ (set (match_operand:SI 0 "general_operand" "=g")
+ (reg:SI 0))]
+ ""
+ "operands[3] = gen_reg_rtx(SImode);")
+
+(define_insn "mul_l"
+ [(set (reg:SI 21)
+ (mult:SI (match_operand:SI 0 "arith_reg_operand" "r")
+ (match_operand:SI 1 "arith_reg_operand" "r")))]
+ "TARGET_SH2"
+ "mul.l %1,%0"
+ [(set_attr "type" "smpy")])
+
+(define_expand "mulsi3"
+ [(set (reg:SI 21)
+ (mult:SI (match_operand:SI 1 "arith_reg_operand" "r")
+ (match_operand:SI 2 "arith_reg_operand" "r")))
+ (set (match_operand:SI 0 "arith_reg_operand" "=r")
+ (reg:SI 21))]
+ ""
+ "
+{
+ if (!TARGET_SH2)
+ {
+ emit_insn (gen_mulsi3_call (operands[0], operands[1], operands[2]));
+ DONE;
+ }
+}")
+
+(define_insn ""
+ [(set (reg:DI 20)
+ (mult:DI (sign_extend:DI (match_operand:SI 1 "arith_reg_operand" "r"))
+ (sign_extend:DI (match_operand:SI 2 "arith_reg_operand" "r"))))]
+ "TARGET_SH2"
+ "dmuls.l %2,%1"
+ [(set_attr "type" "dmpy")])
+
+(define_expand "mulsidi3"
+ [(set (reg:DI 20)
+ (mult:DI (sign_extend:DI (match_operand:SI 1 "arith_reg_operand" "r"))
+ (sign_extend:DI (match_operand:SI 2 "arith_reg_operand" "r"))))
+ (set (match_operand:DI 0 "arith_reg_operand" "=r")
+ (reg:DI 20))]
+ "TARGET_SH2"
+ "")
+
+(define_insn ""
+ [(set (reg:DI 20)
+ (mult:DI (zero_extend:DI (match_operand:SI 1 "arith_reg_operand" "r"))
+ (zero_extend:DI (match_operand:SI 2 "arith_reg_operand" "r"))))]
+ "TARGET_SH2"
+ "dmulu.l %2,%1"
+ [(set_attr "type" "dmpy")])
+
+(define_expand "umulsidi3"
+ [(set (reg:DI 20)
+ (mult:DI (zero_extend:DI (match_operand:SI 1 "arith_reg_operand" "r"))
+ (zero_extend:DI (match_operand:SI 2 "arith_reg_operand" "r"))))
+ (set (match_operand:DI 0 "arith_reg_operand" "=r")
+ (reg:DI 20))]
+ "TARGET_SH2"
+ "")
+
;; -------------------------------------------------------------------------
;; Logical operations
;; -------------------------------------------------------------------------
-(define_insn "andsi3"
+(define_insn "and_ffff"
+ [(set (match_operand:SI 0 "arith_reg_operand" "=r")
+ (and:SI (match_operand:SI 1 "arith_reg_operand" "r")
+ (const_int 65535)))]
+ ""
+ "extu.w %1,%0"
+ [(set_attr "type" "arith")])
+
+(define_insn "and_ff"
+ [(set (match_operand:SI 0 "arith_reg_operand" "=r")
+ (and:SI (match_operand:SI 1 "arith_reg_operand" "r")
+ (const_int 255)))]
+ ""
+ "extu.b %1,%0"
+ [(set_attr "type" "arith")])
+
+(define_insn ""
[(set (match_operand:SI 0 "arith_reg_operand" "=r,z")
(and:SI (match_operand:SI 1 "arith_reg_operand" "%0,0")
(match_operand:SI 2 "logical_operand" "r,L")))]
@@ -318,6 +567,13 @@
"and %2,%0"
[(set_attr "type" "arith")])
+(define_expand "andsi3"
+ [(set (match_operand:SI 0 "arith_reg_operand" "")
+ (and:SI (match_operand:SI 1 "arith_reg_operand" "")
+ (match_operand:SI 2 "logical_operand" "")))]
+ ""
+ "")
+
(define_insn "iorsi3"
[(set (match_operand:SI 0 "arith_reg_operand" "=r,z")
(ior:SI (match_operand:SI 1 "arith_reg_operand" "%0,0")
@@ -326,9 +582,9 @@
"or %2,%0")
(define_insn "xorsi3"
- [(set (match_operand:SI 0 "arith_reg_operand" "=r,z")
+ [(set (match_operand:SI 0 "arith_reg_operand" "=z,r")
(xor:SI (match_operand:SI 1 "arith_reg_operand" "%0,0")
- (match_operand:SI 2 "logical_operand" "r,L")))]
+ (match_operand:SI 2 "logical_operand" "L,r")))]
""
"xor %2,%0"
[(set_attr "type" "arith")])
@@ -370,15 +626,34 @@
""
"{ if (GET_CODE(operands[2]) != CONST_INT || INTVAL(operands[2]) != 1) FAIL;}")
+
+;;
+;; shift left
+
(define_insn "ashlsi3_k"
[(set (match_operand:SI 0 "arith_reg_operand" "=r,r")
(ashift:SI (match_operand:SI 1 "arith_reg_operand" "0,0")
- (match_operand:SI 2 "immediate_operand" "K,n")))
+ (match_operand:SI 2 "immediate_operand" "M,K")))
(clobber (reg:SI 18))]
- ""
+ "CONST_OK_FOR_K (INTVAL (operands[2]))"
+ "@
+ shll %0
+ shll%O2 %0")
+
+; seperate pattern for shifts by any N. Look at pnum_clobbers
+; to see if this is being recognised inside combine. If so, dont
+; match, since combine will try and merge shifts, which will break
+; scheduling
+
+(define_insn "ashlsi3_n"
+ [(set (match_operand:SI 0 "arith_reg_operand" "=r")
+ (ashift:SI (match_operand:SI 1 "arith_reg_operand" "0")
+ (match_operand:SI 2 "immediate_operand" "n")))
+ (clobber (reg:SI 18))]
+ "(! pnum_clobbers)"
"*return output_shift(\"shll\", operands[0], operands[2], ASHIFT);"
- [(set_attr "length" "2,12")
- (set_attr "in_delay_slot" "yes,no")
+ [(set_attr "length" "12")
+ (set_attr "in_delay_slot" "no")
(set_attr "type" "arith")])
(define_expand "ashlsi3"
@@ -387,48 +662,104 @@
(match_operand:SI 2 "immediate_operand" "")))
(clobber (reg:SI 18))])]
""
- "if (!ok_shift_value(operands[2], ASHIFT)) FAIL;")
+ "if (gen_shifty_op (ASHIFT, operands)) DONE; else FAIL;")
+
+;
+; arithmetic shift right
+;
(define_insn "ashrsi3_k"
[(set (match_operand:SI 0 "arith_reg_operand" "=r")
(ashiftrt:SI (match_operand:SI 1 "arith_reg_operand" "0")
- (const_int 1)))
+ (match_operand:SI 2 "immediate_operand" "M")))
(clobber (reg:SI 18))]
- ""
+ "INTVAL(operands[2]) == 1"
"shar %0"
[(set_attr "type" "arith")])
+(define_insn "ashrsi3_16"
+ [(set (match_operand:SI 0 "arith_reg_operand" "=r")
+ (ashiftrt:SI (match_operand:SI 1 "arith_reg_operand" "0")
+ (match_operand:SI 2 "immediate_operand" "i")))
+ (clobber (reg:SI 18))]
+ "INTVAL(operands[2]) == 16"
+ "shlr16 %0\;exts.w %0,%0"
+ [(set_attr "type" "arith")
+ (set_attr "length" "4")])
+
+; an arithmetic shift right by 16 is better as a logical shift and a
+; sign extend
+
+;(define_split
+; [(set (match_operand:SI 0 "arith_reg_operand" "=r")
+; (ashiftrt:SI (match_operand:SI 1 "arith_reg_operand" "0")
+; (const_int 16)))
+; (clobber (reg:SI 18))]
+; ""
+; [(set (match_dup 3) (match_dup 0))
+; (set (match_dup 3) (lshiftrt:SI (match_dup 3) (const_int 16)))
+; (set (match_dup 0) (sign_extend:SI (subreg:HI (match_dup 3) 0)))]
+; "operands[3] = gen_reg_rtx (SImode);")
+
+(define_insn "ashrsi3_n"
+ [(set (reg:SI 4)
+ (ashiftrt:SI (reg:SI 4)
+ (match_operand:SI 0 "immediate_operand" "i")))
+ (clobber (reg:SI 18))
+ (clobber (reg:SI 17))
+ (use (match_operand:SI 1 "arith_reg_operand" "r"))]
+ ""
+ "jsr @%1%#"
+ [(set_attr "type" "sfunc")
+ (set_attr "in_delay_slot" "no")
+ (set_attr "length" "4")
+ (set_attr "needs_delay_slot" "yes")])
+
(define_expand "ashrsi3"
[(parallel[(set (match_operand:SI 0 "arith_reg_operand" "=r")
(ashiftrt:SI (match_operand:SI 1 "arith_reg_operand" "r")
(match_operand:SI 2 "nonmemory_operand" "M")))
(clobber (reg:SI 18))])]
""
- "
-{
- if (GET_CODE (operands[2]) != CONST_INT ||
- INTVAL (operands[2]) != 1) FAIL;
-}
-")
+ "if (gen_shifty_op (ASHIFTRT, operands)) DONE; else FAIL;")
+
+; logical shift right
+;
(define_insn "lshrsi3_k"
[(set (match_operand:SI 0 "arith_reg_operand" "=r,r")
(lshiftrt:SI (match_operand:SI 1 "arith_reg_operand" "0,0")
- (match_operand:SI 2 "immediate_operand" "K,n")))
+ (match_operand:SI 2 "immediate_operand" "M,K")))
(clobber (reg:SI 18))]
- ""
+ "CONST_OK_FOR_K (INTVAL (operands[2]))"
+ "@
+ shlr %0
+ shlr%O2 %0")
+
+; seperate pattern for shifts by any N. Look at pnum_clobbers
+; to see if this is being recognised inside combine. If so, dont
+; match, since combine will try and merge shifts, which will break
+; scheduling - this could be handled with a large number of
+; define_splits
+
+(define_insn "lshrsi3_n"
+ [(set (match_operand:SI 0 "arith_reg_operand" "=r")
+ (lshiftrt:SI (match_operand:SI 1 "arith_reg_operand" "0")
+ (match_operand:SI 2 "immediate_operand" "n")))
+ (clobber (reg:SI 18))]
+ "!pnum_clobbers"
"* return output_shift (\"shlr\", operands[0], operands[2], LSHIFTRT);"
- [(set_attr "length" "2,12")
- (set_attr "in_delay_slot" "yes,no")
+ [(set_attr "length" "12")
+ (set_attr "in_delay_slot" "no")
(set_attr "type" "arith")])
(define_expand "lshrsi3"
[(parallel[(set (match_operand:SI 0 "arith_reg_operand" "")
(lshiftrt:SI (match_operand:SI 1 "arith_reg_operand" "")
- (match_operand:SI 2 "nonmemory_operand" "")))
+ (match_operand:SI 2 "immediate_operand" "")))
(clobber (reg:SI 18))])]
""
- "if (!ok_shift_value (operands[2])) FAIL; ")
+ "if (gen_shifty_op (LSHIFTRT, operands)) DONE; else FAIL;")
(define_insn "ashldi3_k"
[(set (match_operand:DI 0 "arith_reg_operand" "=r")
@@ -485,7 +816,6 @@
"{ if (GET_CODE (operands[2]) != CONST_INT
|| INTVAL (operands[2]) != 1) FAIL; } ")
-
;; -------------------------------------------------------------------------
;; Unary arithmetic
@@ -512,7 +842,7 @@
(not:SI (match_operand:SI 1 "arith_reg_operand" "r")))]
""
"not %1,%0"
-[ (set_attr "type" "arith")])
+ [(set_attr "type" "arith")])
;; -------------------------------------------------------------------------
@@ -546,170 +876,187 @@
;; -------------------------------------------------------------------------
(define_insn "extendsidi2"
- [(set (match_operand:DI 0 "arith_reg_operand" "=r")
- (sign_extend:DI (match_operand:SI 1 "arith_reg_operand" "0")))]
+ [(set (match_operand:DI 0 "arith_reg_operand" "=r,r")
+ (sign_extend:DI (match_operand:SI 1 "arith_reg_operand" "0,r")))
+ (clobber (reg:SI 18))]
""
- "mov %1,%0\;shll %0\;subc %0,%0"
- [(set_attr "length" "6")])
+ "@
+ mov %1,%0\;shll %0\;subc %0,%0 ! b sidi2
+ mov %1,%0\;mov %1,%R0\;shll %0\;subc %0,%0 ! a sidi2"
+ [(set_attr "length" "6,8")])
(define_insn "extendhisi2"
- [(set (match_operand:SI 0 "arith_reg_operand" "=r")
- (sign_extend:SI (match_operand:HI 1 "arith_reg_operand" "r")))]
+ [(set (match_operand:SI 0 "arith_reg_operand" "=r,z,r")
+ (sign_extend:SI (match_operand:HI 1 "arith_operand" "r,u,m")))]
""
- "exts.w %1,%0")
+ "@
+ exts.w %1,%0
+ mov.w %1,%0
+ mov.w %1,%0"
+ [(set_attr "type" "arith,load,load")])
(define_insn "extendqisi2"
- [(set (match_operand:SI 0 "arith_reg_operand" "=r")
- (sign_extend:SI (match_operand:QI 1 "arith_reg_operand" "r")))]
+ [(set (match_operand:SI 0 "arith_reg_operand" "=r,z,r")
+ (sign_extend:SI (match_operand:QI 1 "general_movsrc_operand" "r,U,m")))]
""
- "exts.b %1,%0")
+ "@
+ exts.b %1,%0
+ mov.b %1,%0 !p9
+ mov.b %1,%0 !p8"
+ [(set_attr "type" "arith,load,load")])
(define_insn "extendqihi2"
- [(set (match_operand:HI 0 "arith_reg_operand" "=r")
- (sign_extend:HI (match_operand:QI 1 "arith_reg_operand" "r")))]
+ [(set (match_operand:HI 0 "arith_reg_operand" "=r,z,r")
+ (sign_extend:HI (match_operand:QI 1 "general_movsrc_operand" "r,U,m")))]
""
- "exts.b %1,%0")
+ "@
+ exts.b %1,%0
+ mov.b %1,%0 !p7
+ mov.b %1,%0 ! p6"
+ [(set_attr "type" "arith,load,load")])
;; -------------------------------------------------------------------------
;; Move instructions
;; -------------------------------------------------------------------------
-(define_insn ""
- [(set (match_operand:SI 0 "push_operand" "=<,<")
- (match_operand:SI 1 "arith_reg_operand" "r,l"))]
- ""
- "@
- mov.l %1,%0
- sts.l pr,%0"
- [(set_attr "type" "store")])
-
-(define_insn ""
- [(set (match_operand:SI 0 "arith_reg_operand" "=r,l")
- (match_operand:SI 1 "pop_operand" "=>,>"))]
- ""
- "@
- mov.l %1,%0
- lds.l %1,pr"
- [(set_attr "type" "load,pload")])
+;; define push and pop so it is easy for sh.c
(define_insn "push"
[(set (mem:SI (pre_dec:SI (reg:SI 15)))
- (match_operand:SI 0 "register_operand" "r,l"))]
+ (match_operand:SI 0 "register_operand" "r,lx"))]
""
"@
mov.l %0,@-r15
- sts.l pr,@-r15")
+ sts.l %0,@-r15 ! push"
+ [(set_attr "type" "store")
+ (set_attr "hit_stack" "yes")])
(define_insn "pop"
- [(set (match_operand:SI 0 "register_operand" "=r,l")
+ [(set (match_operand:SI 0 "register_operand" "=r,lx")
(mem:SI (post_inc:SI (reg:SI 15))))]
""
"@
mov.l @r15+,%0
- lds.l @r15+,pr"
- [(set_attr "type" "load,pload")])
-
-; some constants are easier to generate with alu operations
-; rather than loading from memory
+ lds.l @r15+,%0"
+ [(set_attr "type" "load,pload")
+ (set_attr "hit_stack" "yes")])
-(define_split
- [(set (match_operand:SI 0 "register_operand" "=r")
- (match_operand:SI 1 "painful_immediate_operand" "i"))]
+(define_insn ""
+ [(set (match_operand:SI 0 "push_operand" "=<,<")
+ (match_operand:SI 1 "arith_reg_operand" "r,xl"))]
""
- [(set (match_dup 0) (const_int 127))
- (set (match_dup 0) (plus:SI (match_dup 0)
- (match_dup 2)))]
- "operands[2] = GEN_INT (INTVAL(operands[1]) - 127);" )
+ "@
+ mov.l %1,%0
+ sts.l %1,%0"
+ [(set_attr "type" "store")])
-(define_insn "movsi_i"
- [(set (match_operand:SI 0 "general_operand" "=r,r,r,m,l,r,r,r,t,x")
- (match_operand:SI 1 "general_movsrc_operand" "r,I,m,r,r,l,t,x,r,r"))]
+(define_insn ""
+ [(set (match_operand:SI 0 "arith_reg_operand" "=r,xl")
+ (match_operand:SI 1 "pop_operand" "=>,>"))]
""
"@
- mov %1,%0
- mov %1,%0
mov.l %1,%0
- mov.l %1,%0
- lds %1,%0
- sts %1,%0
- movt %0
- sts %1,%0
- tst %1,%1\;bt T%*\;bra F%*\;sett\;T%*:clrt\;F%*:%^
- lds %1,%0"
- [(set_attr "length" "2,2,2,2,2,2,2,2,10,2")
- (set_attr "type" "move,move,load,pload,move,move,move,move,move,move")])
+ lds.l %1,%0"
+ [(set_attr "type" "load")])
-(define_insn "movsi_pcrel"
- [(set (match_operand:SI 0 "arith_reg_operand" "=r")
- (match_operand:SI 1 "hard_immediate_operand" "i"))]
+(define_insn "clrt"
+ [(set (reg:SI 18) (const_int 0))]
""
- "* return output_movepcrel (insn, operands, SImode);"
- [(set_attr "length" "2")
- (set_attr "in_delay_slot" "no")
- (set_attr "constneed" "yes")
- (set_attr "smallestsize" "2")
- (set_attr "largestsize" "8")
- (set_attr "type" "load")])
+ "clrt")
+(define_insn "movsi_i"
+ [(set (match_operand:SI 0 "general_movdst_operand" "=r,r,r,r,r,<m,<,xl,xl,t,r")
+ (match_operand:SI 1 "general_movsrc_operand" "Q,rI,>m,xl,t,r,xl,r,>,r,i"))]
+ ""
+ "*
+{
+ switch (which_alternative)
+ {
+ case 0:
+ switch (get_attr_length(insn))
+ {
+ case 2:
+ return \"mov.l %1,%0 ! 2 byte\";
+ case 12:
+ return \"mov.l TA%*,%0\;bra TB%*\;mov.l @%0,%0\;.align 2\;TA%*: .long %1\;TB%*:%^\";
+ }
+ case 1: return \"mov %1,%0\";
+ case 2: return \"mov.l %1,%0\";
+ case 3: return \"sts %1,%0\";
+ case 4: return \"movt %0\";
+ case 5: return \"mov.l %1,%0\";
+ case 6: return \"sts.l %1,%0\";
+ case 7: return \"lds %1,%0\";
+ case 8: return \"lds.l %1,%0\";
+ case 9: return \"tst %1,%1\;bt T%*\;bra F%*\;sett\;T%*:clrt\;F%*:%^\";
+ case 10: return \"fake %1,%0\";
+ }
+}"
+ [(set_attr "length" "*,2,2,2,2,2,2,2,2,6,2")
+ (set_attr "type" "pcloadsi,move,load,move,store,store,move,load,move,move,move")])
+
(define_expand "movsi"
- [(set (match_operand:SI 0 "general_operand" "")
- (match_operand:SI 1 "general_operand" ""))]
+ [(set (match_operand:SI 0 "general_movdst_operand" "")
+ (match_operand:SI 1 "general_movsrc_operand" ""))]
""
- "{ if(prepare_move_operands(operands, SImode)) DONE; } ")
+ "{ if (prepare_move_operands(operands, SImode)) DONE; } ")
(define_insn "movqi_i"
- [(set (match_operand:QI 0 "general_operand" "=r,r,r,m,r,m,r,r")
- (match_operand:QI 1 "general_operand" "r,n,m,r,m,r,x,t"))]
- ""
+ [(set (match_operand:QI 0 "general_movdst_operand" "=r,r,>m,r,r,l")
+ (match_operand:QI 1 "general_movsrc_operand" "ri,<m,r,t,l,r"))]
+ "arith_reg_operand (operands[0], QImode)
+ || arith_reg_operand (operands[1], QImode)"
"@
mov %1,%0
- mov %1,%0
- mov.b %1,%0
- mov.b %1,%0
- mov.b %1,%0
- mov.b %1,%0
+ mov.b %1,%0
+ mov.b %1,%0
+ movt %0
sts %1,%0
- movt %0")
+ lds %1,%0"
+ [(set_attr "length" "2,2,2,2,2,2")
+ (set_attr "type" "move,load,store,move,move,move")])
(define_expand "movqi"
[(set (match_operand:QI 0 "general_operand" "")
(match_operand:QI 1 "general_operand" ""))]
""
- "prepare_move_operands(operands, QImode);")
-
-(define_insn "movhi_pcrel"
- [(set (match_operand:HI 0 "arith_reg_operand" "=r")
- (match_operand:HI 1 "hard_immediate_operand" "i"))]
- ""
- "* return output_movepcrel (insn, operands, SImode);"
- [(set_attr "length" "2")
- (set_attr "in_delay_slot" "no")
- (set_attr "constneed" "yes")
- (set_attr "smallestsize" "2")
- (set_attr "largestsize" "8")
- (set_attr "type" "load")])
+ "if (prepare_move_operands(operands, QImode)) DONE; ")
(define_insn "movhi_i"
- [(set (match_operand:HI 0 "general_operand" "=r,r,m,z,m,r,r")
- (match_operand:HI 1 "general_operand" "rI,m,r,m,z,x,t"))]
+ [(set (match_operand:HI 0 "general_movdst_operand" "=r,r,r,r,<m,r,r,l")
+ (match_operand:HI 1 "general_movsrc_operand" "Q,rI,>m,t,r,i,l,r"))]
""
- "@
- mov %1,%0
- mov.w %1,%0
- mov.w %1,%0
- mov.w %1,%0
- mov.w %1,%0
- sts %1,%0
- movt %0"
- [(set_attr "type" "move,load,store,load,store,move,move")])
+ "*
+{
+ switch (which_alternative)
+ {
+ case 0:
+ switch (get_attr_length(insn))
+ {
+ case 2:
+ return \"mov.w %1,%0 ! 2 byte\";
+ case 12:
+ return \"mov.l TA%*,%0\;bra TB%*\;mov.w @%0,%0\;.align 2\;TA%*: .long %1\;TB%*:%^\";
+ }
+ case 1: return \"mov %1,%0\";
+ case 2: return \"mov.w %1,%0\";
+ case 3: return \"movt %0\";
+ case 4: return \"mov.w %1,%0\";
+ case 5: return \"fake %1,%0\";
+ case 6: return \"sts %1,%0\";
+ case 7: return \"lds %1,%0\";
+ }
+}"
+ [(set_attr "length" "*,2,2,2,2,2,2,2")
+ (set_attr "type" "pcloadhi,move,load,move,store,move,move,move")])
(define_expand "movhi"
- [(set (match_operand:HI 0 "general_operand" "")
- (match_operand:HI 1 "general_operand" ""))]
+ [(set (match_operand:HI 0 "general_movdst_operand" "")
+ (match_operand:HI 1 "general_movsrc_operand" ""))]
""
- "prepare_move_operands (operands, HImode);")
-
+ "if (prepare_move_operands (operands, HImode)) DONE;")
+
(define_insn ""
[(set (match_operand:DI 0 "push_operand" "=<")
(match_operand:DI 1 "arith_reg_operand" "r"))]
@@ -718,68 +1065,59 @@
[(set_attr "length" "4")
(set_attr "type" "store")])
-(define_insn "movdi_pcrel"
- [(set (match_operand:DI 0 "arith_reg_operand" "=r")
- (match_operand:DI 1 "hard_immediate_operand" "i"))]
- ""
- "* return output_movepcrel (insn, operands, DImode);"
- [(set_attr "length" "4")
- (set_attr "in_delay_slot" "no")
- (set_attr "constneed" "yes")
- (set_attr "smallestsize" "4")
- (set_attr "constantsize" "8")
- (set_attr "largestsize" "18")
- (set_attr "type" "load")])
-
-(define_insn "movdi_k"
- [(set (match_operand:DI 0 "general_operand" "=r,r,m,r,r,m,r")
- (match_operand:DI 1 "general_operand" "r,m,r,I,m,r,x"))]
+(define_split
+ [(set (match_operand:DI 0 "general_movdst_operand" "")
+ (match_operand:DI 1 "nonimmediate_operand" ""))]
""
- "* return output_movedouble(operands, DImode);"
- [(set_attr "length" "4")
- (set_attr "type" "move,load,store,move,load,store,load")])
+ [(set (match_dup 2) (match_dup 3))
+ (set (match_dup 4) (match_dup 5))]
+ "if (prepare_split_double_ops (operands, DImode)) DONE; else FAIL;")
+(define_insn ""
+ [(set (match_operand:DI 0 "general_movdst_operand" "=r,r,r,m,r")
+ (match_operand:DI 1 "general_movsrc_operand" "Q,r,m,r,i"))]
+ "register_operand (operands[0], DImode)
+ || register_operand (operands[1], DImode)"
+ "* return output_movedouble (insn, operands, DImode);"
+ [(set_attr "length" "*,4,4,4,4")
+ (set_attr "type" "pcloadsi,move,load,store,move")])
(define_expand "movdi"
- [(set (match_operand:DI 0 "general_operand" "")
- (match_operand:DI 1 "general_operand" ""))]
+ [(set (match_operand:DI 0 "general_movdst_operand" "=r,m,r")
+ (match_operand:DI 1 "general_movsrc_operand" "m,r,i"))]
""
- "prepare_move_operands (operands, DImode);")
+ "if (prepare_move_operands(operands, DImode)) DONE;")
(define_insn ""
[(set (match_operand:DF 0 "push_operand" "=<")
(match_operand:DF 1 "arith_reg_operand" "r"))]
""
- "mov.l %R1,%0\;mov.l %1,%0"
+ "mov.l %R1,%0\;mov.l %1,%0"
[(set_attr "length" "4")
(set_attr "type" "store")])
-(define_insn "movdf_pcrel"
- [(set (match_operand:DF 0 "arith_reg_operand" "=r")
- (match_operand:DF 1 "hard_immediate_operand" "i"))]
+(define_split
+ [(set (match_operand:DF 0 "general_movdst_operand" "")
+ (match_operand:DF 1 "nonimmediate_operand" ""))]
""
- "* return output_movepcrel (insn, operands, DFmode);"
- [(set_attr "length" "4")
- (set_attr "in_delay_slot" "no")
- (set_attr "constneed" "yes")
- (set_attr "smallestsize" "4")
- (set_attr "constantsize" "8")
- (set_attr "largestsize" "18")
- (set_attr "type" "load")])
+ [(set (match_dup 2) (match_dup 3))
+ (set (match_dup 4) (match_dup 5))]
+ "if (prepare_split_double_ops (operands, DFmode)) DONE; else FAIL;")
(define_insn "movdf_k"
- [(set (match_operand:DF 0 "general_operand" "=r,r,m")
- (match_operand:DF 1 "general_operand" "r,m,r"))]
- ""
- "* return output_movedouble(operands, DFmode);"
+ [(set (match_operand:DF 0 "general_movdst_operand" "=r,r,m")
+ (match_operand:DF 1 "general_movsrc_operand" "r,m,r"))]
+ "register_operand (operands[0], DFmode)
+ || register_operand (operands[1], DFmode)"
+ "* return output_movedouble (insn, operands, DFmode);"
[(set_attr "length" "4")
(set_attr "type" "move,load,store")])
(define_expand "movdf"
- [(set (match_operand:DF 0 "general_operand" "")
- (match_operand:DF 1 "general_operand" ""))]
+ [(set (match_operand:DF 0 "general_movdst_operand" "")
+ (match_operand:DF 1 "general_movsrc_operand" ""))]
""
- "prepare_move_operands(operands, DFmode);")
+ "if (prepare_move_operands (operands, DFmode)) DONE;")
(define_insn ""
[(set (match_operand:SF 0 "push_operand" "=<")
@@ -787,39 +1125,28 @@
""
"mov.l %1,%0"
[(set_attr "type" "store")])
-
-(define_insn "movsf_pcrel"
- [(set (match_operand:SF 0 "arith_reg_operand" "=r")
- (match_operand:SF 1 "hard_immediate_operand" "i"))]
- ""
- "* return output_movepcrel (insn, operands, SFmode);"
- [(set_attr "length" "2")
- (set_attr "in_delay_slot" "no")
- (set_attr "constneed" "yes")
- (set_attr "smallestsize" "2")
- (set_attr "largestsize" "8")
- (set_attr "type" "load")])
(define_insn "movsf_i"
- [(set (match_operand:SF 0 "general_operand" "=r,r,r,m,l,r,m,r")
- (match_operand:SF 1 "general_operand" "r,I,m,r,r,l,r,m"))]
+ [(set (match_operand:SF 0 "general_movdst_operand" "=>,r,r,r,r,m,l,r")
+ (match_operand:SF 1 "general_movsrc_operand" "r,<,r,I,m,r,r,l"))]
""
"@
+ mov.l %1,@%N0\;add #4,%N0
+ add #-4,%1\;mov.l @%N1,%0
mov %1,%0
mov %1,%0
mov.l %1,%0
mov.l %1,%0
lds %1,%0
- sts %1,%0
- mov %1,%0
- mov %1,%0"
- [(set_attr "type" "move,move,load,store,move,move,move,move")])
+ sts %1,%0"
+ [(set_attr "type" "store,load,move,move,load,store,move,move")
+ (set_attr "length" "4,4,*,*,*,*,*,*")])
(define_expand "movsf"
- [(set (match_operand:SF 0 "general_operand" "")
- (match_operand:SF 1 "general_operand" ""))]
+ [(set (match_operand:SF 0 "general_movdst_operand" "")
+ (match_operand:SF 1 "general_movsrc_operand" ""))]
""
- "prepare_move_operands(operands, SFmode);")
+ "if (prepare_move_operands (operands, SFmode)) DONE;")
;; ------------------------------------------------------------------------
@@ -839,7 +1166,7 @@
(label_ref (match_operand 0 "" ""))
(pc)))]
""
- "* return output_branch (0, insn);"
+ "* return output_branch (0, insn, operands[1]);"
[(set_attr "type" "cbranch")])
(define_insn "inverse_branch_true"
@@ -869,12 +1196,7 @@
(label_ref (match_operand 0 "" ""))
(pc)))]
""
- "
-{
- operands[1] = sh_compare_op0;
- operands[2] = sh_compare_op1;
-}")
-
+ "from_compare (operands, EQ);")
; There is no bne compare, so we reverse the branch arms.
@@ -886,11 +1208,7 @@
(pc)
(label_ref (match_operand 0 "" ""))))]
""
- "
-{
- operands[1] = sh_compare_op0;
- operands[2] = sh_compare_op1;
-}")
+ "from_compare (operands, NE);")
(define_expand "bgt"
[(set (reg:SI 18) (gt:SI (match_dup 1) (match_dup 2)))
@@ -898,41 +1216,29 @@
(if_then_else (eq (reg:SI 18)
(const_int 1))
(label_ref (match_operand 0 "" ""))
- (pc)))]
+ (pc))) ]
""
- "
-{
- operands[1] = sh_compare_op0;
- operands[2] = sh_compare_op1;
-}")
+ "from_compare (operands, GT);")
(define_expand "blt"
- [(set (reg:SI 18) (lt:SI (match_dup 1) (match_dup 2)))
+ [(set (reg:SI 18) (ge:SI (match_dup 1) (match_dup 2)))
(set (pc)
- (if_then_else (eq (reg:SI 18)
- (const_int 1))
- (label_ref (match_operand 0 "" ""))
- (pc)))]
+ (if_then_else (eq (reg:SI 18)(const_int 1))
+ (pc)
+ (label_ref (match_operand 0 "" ""))))]
""
- "
-{
- operands[1] = sh_compare_op0;
- operands[2] = sh_compare_op1;
-}")
+ "from_compare (operands, LT);")
(define_expand "ble"
- [(set (reg:SI 18) (le:SI (match_dup 1) (match_dup 2)))
- (set (pc)
+ [(set (reg:SI 18) (gt:SI (match_dup 1) (match_dup 2)))
+ (set (pc)
(if_then_else (eq (reg:SI 18)
(const_int 1))
- (label_ref (match_operand 0 "" ""))
- (pc)))]
+ (pc)
+ (label_ref (match_operand 0 "" ""))))
+ ]
""
- "
-{
- operands[1] = sh_compare_op0;
- operands[2] = sh_compare_op1;
-}")
+ "from_compare (operands, LE);")
(define_expand "bge"
[(set (reg:SI 18) (ge:SI (match_dup 1) (match_dup 2)))
@@ -940,84 +1246,65 @@
(if_then_else (eq (reg:SI 18)
(const_int 1))
(label_ref (match_operand 0 "" ""))
- (pc)))]
+ (pc))) ]
""
- "
-{
- operands[1] = sh_compare_op0;
- operands[2] = sh_compare_op1;
-}")
+ "from_compare (operands, GE);")
(define_expand "bgtu"
[(set (reg:SI 18) (gtu:SI (match_dup 1) (match_dup 2)))
(set (pc)
(if_then_else (eq (reg:SI 18)
- (const_int 1))
+ (const_int 1))
(label_ref (match_operand 0 "" ""))
(pc)))]
""
- "
-{
- operands[1] = sh_compare_op0;
- operands[2] = sh_compare_op1;
-}")
+ "from_compare (operands, GTU); ")
(define_expand "bltu"
- [(set (reg:SI 18) (ltu:SI (match_dup 1) (match_dup 2)))
+ [(set (reg:SI 18) (geu:SI (match_dup 1) (match_dup 2)))
(set (pc)
- (if_then_else (eq (reg:SI 18)
- (const_int 1))
- (label_ref (match_operand 0 "" ""))
- (pc)))]
+ (if_then_else (eq (reg:SI 18)
+ (const_int 1))
+ (pc)
+ (label_ref (match_operand 0 "" ""))))]
""
- "
-{
- operands[1] = sh_compare_op0;
- operands[2] = sh_compare_op1;
-}")
+ "from_compare (operands, LTU);")
(define_expand "bgeu"
[(set (reg:SI 18) (geu:SI (match_dup 1) (match_dup 2)))
(set (pc)
(if_then_else (eq (reg:SI 18)
- (const_int 1))
+ (const_int 1))
(label_ref (match_operand 0 "" ""))
- (pc)))]
+ (pc)))
+ ]
""
- "
-{
- operands[1] = sh_compare_op0;
- operands[2] = sh_compare_op1;
-}")
+ "from_compare (operands, GEU);")
(define_expand "bleu"
- [(set (reg:SI 18) (leu:SI (match_dup 1) (match_dup 2)))
+ [(set (reg:SI 18) (gtu:SI (match_dup 1) (match_dup 2)))
(set (pc)
(if_then_else (eq (reg:SI 18)
- (const_int 1))
- (label_ref (match_operand 0 "" ""))
- (pc)))]
+ (const_int 1))
+ (pc)
+ (label_ref (match_operand 0 "" ""))))]
""
- "
-{
- operands[1] = sh_compare_op0;
- operands[2] = sh_compare_op1;
-}")
+ "from_compare (operands, LEU);")
;; ------------------------------------------------------------------------
;; Jump and linkage insns
;; ------------------------------------------------------------------------
-(define_insn "jump_real"
+(define_insn "jump"
[(set (pc)
(label_ref (match_operand 0 "" "")))]
""
"*
{
- if (get_attr_length(insn) == 6)
+ if (get_attr_length(insn) == 10)
{
- return \"mov.l %I0,r13\;jmp @r13%#\";
+ return output_far_jump(insn, operands[0]);
}
else
{
@@ -1027,15 +1314,6 @@
[(set_attr "type" "jump")
(set_attr "needs_delay_slot" "yes")])
-(define_expand "jump"
- [(set (pc) (label_ref (match_operand 0 "" "")))]
- ""
- "
-{
- emit_insn(gen_jump_real(operand0));
- DONE;
-}
-")
(define_insn "calli"
[(call (mem:SI (match_operand:SI 0 "arith_reg_operand" "r"))
@@ -1063,16 +1341,7 @@
(match_operand 1 "" ""))
(clobber (reg:SI 17))])]
""
- "
-{
- if (GET_CODE (operands[0]) == MEM)
- {
- operands[0]
- = gen_rtx(MEM,GET_MODE (operands[0]),
- force_reg (Pmode,
- XEXP (operands[0], 0)));
- }
-}")
+ "expand_acall(0, operands); DONE;")
(define_expand "call_value"
[(parallel[(set (match_operand 0 "" "=rf")
@@ -1080,16 +1349,7 @@
(match_operand 2 "" "")))
(clobber (reg:SI 17))])]
""
- "
-{
- if (GET_CODE (operands[1]) == MEM)
- {
- operands[1]
- = gen_rtx (MEM, GET_MODE (operands[1]),
- force_reg (Pmode,
- XEXP (operands[1], 0)));
- }
-}")
+ "expand_acall(1, operands); DONE; ")
(define_insn "indirect_jump"
[(set (pc)
@@ -1100,36 +1360,107 @@
(set_attr "in_delay_slot" "no")
(set_attr "length" "4")])
-
;; ------------------------------------------------------------------------
;; Misc insns
;; ------------------------------------------------------------------------
+(define_insn "dect"
+ [(parallel[
+ (set (reg:SI 18)
+ (eq:SI (match_operand:SI 0 "register_operand" "=r")
+ (const_int 1)))
+
+ (set (match_dup 0)
+ (plus:SI (match_dup 0)
+ (const_int -1)))])]
+ "TARGET_SH2"
+ "dt %0")
(define_insn "nop"
[(const_int 0)]
""
"or r0,r0")
-(define_insn "tablejump"
- [(set (pc)
- (match_operand:SI 0 "arith_reg_operand" "r"))
- (use (label_ref (match_operand 1 "" "")))]
+; experimental use of auto inc and dec made these...
+; can be deleted
+
+(define_insn "fake"
+ [(set (match_operand:QI 0 "register_operand" "=r")
+ (mem:QI (pre_dec:SI (match_operand:SI 1 "register_operand" "r"))))]
""
- "!table jump\;jmp @%0\;or r0,r0\;%!"
+ "add #-1,%1\;mov.b @%1,%0"
+ [(set_attr "length" "4")])
+
+;; Load address of a label. This is only generated by the casesi expand.
+
+(define_insn "mova"
+ [(set (reg:SI 0)
+ (label_ref (match_operand 0 "" "")))]
+ ""
+ "mova %O0,r0"
+ [(set_attr "in_delay_slot" "no")])
+
+;; case instruction for switch statements.
+
+;; Operand 0 is index
+;; operand 1 is the minimum bound
+;; operand 2 is the maximum bound - minimum bound + 1
+;; operand 3 is CODE_LABEL for the table;
+;; operand 4 is the CODE_LABEL to go to if index out of range.
+
+(define_expand "casesi"
+ [(set (match_dup 5) (match_operand:SI 0 "arith_reg_operand" ""))
+ (set (match_dup 5) (minus:SI (match_dup 5)
+ (match_operand:SI 1 "arith_operand" "")))
+ (set (reg:SI 18)
+ (gtu:SI (match_dup 5)
+ (match_operand:SI 2 "arith_operand" "")))
+ (set (pc)
+ (if_then_else (eq (reg:SI 18)
+ (const_int 1))
+ (label_ref (match_operand 4 "" ""))
+ (pc)))
+ (set (match_dup 6) (plus:SI (match_dup 5) (match_dup 5)))
+ (set (reg:SI 0) (label_ref (match_operand 3 "" "")))
+ (parallel[(set (reg:SI 0) (plus:SI (reg:SI 0)
+ (mem:HI (plus:SI (reg:SI 0)
+ (match_dup 6)))))
+ (set (match_dup 6) (mem:HI (plus:SI (reg:SI 0) (match_dup 6))))])
+ (set (pc) (reg:SI 0))]
+ ""
+ "
+{
+ operands[1] = copy_to_mode_reg (SImode, operands[1]);
+ operands[2] = copy_to_mode_reg (SImode, operands[2]);
+ operands[5] = gen_reg_rtx (SImode);
+ operands[6] = gen_reg_rtx (SImode);
+}")
+
+(define_insn "casesi_worker"
+ [(set (reg:SI 0)
+ (plus:SI (reg:SI 0)
+ (mem:HI (plus:SI (reg:SI 0)
+ (match_operand:SI 0 "register_operand" "=r")))))
+ (set (match_dup 0) (mem:HI (plus:SI (reg:SI 0)
+ (match_dup 0))))]
+ ""
+ "mov.w @(r0,%0),%0\;add %0,r0"
[(set_attr "needs_delay_slot" "no")
(set_attr "in_delay_slot" "no")
- (set_attr "type" "jump")
- (set_attr "dump" "no")])
+ (set_attr "length" "6")])
-(define_insn "return"
+(define_insn ""
[(return)]
- "reload_completed"
- "rts %#"
- [(set_attr "type" "return")
- (set_attr "needs_delay_slot" "yes")
- (set_attr "dump" "yes")])
+ "!pragma_interrupt && reload_completed"
+ "rts%#"
+ [(set_attr "type" "return")])
+
+(define_insn ""
+ [(return)]
+ "pragma_interrupt && reload_completed"
+ "rte%#"
+ [(set_attr "type" "rte")])
(define_expand "prologue"
[(const_int 0)]
@@ -1152,7 +1483,7 @@
;; Scc instructions
;; ------------------------------------------------------------------------
-(define_insn ""
+(define_insn "movt"
[(set (match_operand:SI 0 "arith_reg_operand" "=r")
(eq (reg:SI 18) (const_int 1)))]
""
@@ -1219,59 +1550,6 @@
""
"operands[1] = prepare_scc_operands (EQ);")
-; these patterns give better code then gcc invents if
-; left to its own devices
-
-(define_insn "anddi3"
- [(set (match_operand:DI 0 "arith_reg_operand" "=r")
- (and:DI (match_operand:DI 1 "arith_reg_operand" "%0")
- (match_operand:DI 2 "arith_reg_operand" "r")))]
- ""
- "and %2,%0\;and %R2,%R0"
- [(set_attr "length" "4")])
-
-(define_insn "iordi3"
- [(set (match_operand:DI 0 "arith_reg_operand" "=r")
- (ior:DI (match_operand:DI 1 "arith_reg_operand" "%0")
- (match_operand:DI 2 "arith_reg_operand" "r")))]
- ""
- "or %2,%0\;or %R2,%R0"
- [(set_attr "length" "4")])
-
-(define_insn "xordi3"
- [(set (match_operand:DI 0 "arith_reg_operand" "=r")
- (xor:DI (match_operand:DI 1 "arith_reg_operand" "%0")
- (match_operand:DI 2 "arith_reg_operand" "r")))]
- ""
- "xor %2,%0\;xor %R2,%R0"
- [(set_attr "length" "4")])
-
-
-;; ------------------------------------------------------------------------
-;; Block move
-;; ------------------------------------------------------------------------
-
-(define_expand "movstrsi"
- [(parallel [(set (mem:BLK (match_operand:BLK 0 "general_operand" ""))
- (mem:BLK (match_operand:BLK 1 "general_operand" "")))
- (use (match_operand:SI 2 "general_operand" ""))
- (use (match_operand:SI 3 "immediate_operand" ""))
- ])]
- ""
- "
-{
- rtx dest_mem = operands[0];
- rtx src_mem = operands[1];
- operands[0] = copy_to_mode_reg (SImode, XEXP (operands[0], 0));
- operands[1] = copy_to_mode_reg (SImode, XEXP (operands[1], 0));
- expand_block_move (dest_mem, src_mem, operands);
- DONE;
-}")
-
-
-
-
-
;; -------------------------------------------------------------------------
;; Peepholes
;; -------------------------------------------------------------------------
@@ -1281,39 +1559,39 @@
[(set (match_operand:QI 0 "arith_reg_operand" "")
(mem:QI (match_operand:SI 1 "arith_reg_operand" "")))
(set (match_dup 1) (plus:SI (match_dup 1) (const_int 1)))]
- "REGNO(operands[1]) != REGNO(operands[0])"
+ "REGNO (operands[1]) != REGNO (operands[0])"
"mov.b @%1+,%0")
(define_peephole
[(set (match_operand:HI 0 "arith_reg_operand" "")
(mem:HI (match_operand:SI 1 "arith_reg_operand" "")))
(set (match_dup 1) (plus:SI (match_dup 1) (const_int 2)))]
- "REGNO(operands[1]) != REGNO(operands[0])"
+ "REGNO (operands[1]) != REGNO (operands[0])"
"mov.w @%1+,%0")
(define_peephole
[(set (match_operand:SI 0 "arith_reg_operand" "")
(mem:SI (match_operand:SI 1 "arith_reg_operand" "")))
(set (match_dup 1) (plus:SI (match_dup 1) (const_int 4)))]
- "REGNO(operands[1]) != REGNO(operands[0])"
+ "REGNO (operands[1]) != REGNO (operands[0])"
"mov.l @%1+,%0")
(define_peephole
[(set (match_operand:QI 0 "register_operand" "=r")
- (match_operand:QI 1 "general_operand" "g"))
+ (match_operand:QI 1 "memory_operand" "g"))
(set (match_operand:SI 2 "register_operand" "=r")
(sign_extend:SI (match_dup 0)))]
- "REGNO(operands[0]) == REGNO(operands[2])"
- "mov.b %1,%0")
+ "REGNO (operands[0]) == REGNO (operands[2])"
+ "mov.b %1,%0 !p 5")
(define_peephole
[(set (match_operand:QI 0 "register_operand" "=r")
- (match_operand:QI 1 "general_operand" "g"))
+ (match_operand:QI 1 "general_movsrc_operand" "g"))
(set (match_operand:SI 2 "register_operand" "=r")
(sign_extend:SI (match_dup 0)))]
- "REGNO(operands[0]) != REGNO(operands[2])
+ "REGNO (operands[0]) != REGNO (operands[2])
&& dead_or_set_p (insn, operands[0])"
- "mov.b %1,%2")
+ "mov.b %1,%2 ! p4")
; notice when a mov.b could be done with a displacement
@@ -1322,7 +1600,7 @@
(plus:SI (match_dup 0)
(match_operand:SI 1 "byte_index_operand" "i")))
(set (mem:QI (match_dup 0)) (reg:QI 0))]
- "dead_or_set_p(insn, operands[0])"
+ "dead_or_set_p (insn, operands[0])"
"mov.b r0,@(%O1,%0)")
(define_peephole
@@ -1330,9 +1608,294 @@
(plus:SI (match_dup 0)
(match_operand:SI 1 "byte_index_operand" "i")))
(set (reg:QI 0) (mem:QI (match_dup 0)))]
- "dead_or_set_p(insn, operands[0])"
+ "dead_or_set_p (insn, operands[0])"
"mov.b @(%O1,%0),r0")
+;; -------------------------------------------------------------------------
+;; Peepholes
+;; -------------------------------------------------------------------------
+
+(define_peephole
+ [(set (reg:SI 0) (label_ref (match_operand 0 "" "")))
+ (set (match_operand:SI 1 "register_operand" "=r")
+ (reg:SI 0))
+ (set (reg:SI 0) (label_ref (match_dup 0)))
+ (set (match_operand:SI 2 "register_operand" "=r")
+ (reg:SI 0))]
+ ""
+ "mova %O0,r0\;mov r0,%1\;mov r0,%2"
+ [(set_attr "length" "6")
+ (set_attr "in_delay_slot" "no")])
+
+
+; mov r4,r3
+; shll r3
+; mov r3,r0
+;->
+; mov r4,r0
+; shll r0
+
+(define_peephole
+ [(set (match_operand:SI 0 "register_operand" "=r")
+ (match_operand:SI 1 "register_operand" "r"))
+ (set (match_dup 0)
+ (plus:SI (match_dup 0) (match_dup 1)))
+ (set (match_operand:SI 2 "register_operand" "=r")
+ (match_dup 0))]
+ "dead_or_set_p (NEXT_INSN (insn), operands[0])"
+ "mov %1,%2\;add %2,%2"
+ [(set_attr "length" "4")])
+
+(define_peephole
+ [(set (match_operand:SI 0 "arith_reg_operand" "=r")
+ (and:SI (match_dup 0)
+ (const_int 1)))
+ (set (match_operand:SI 1 "arith_reg_operand" "=r")
+ (const_int 0))
+ (set (reg:SI 18)
+ (eq:SI (match_dup 0) (match_dup 1)))]
+ "dead_or_set_p (insn, operands[0])
+ && dead_or_set_p (insn, operands[1])"
+ "rotr %0")
+
+(define_peephole
+ [(set (match_operand:SI 0 "arith_reg_operand" "z,r")
+ (and:SI (match_dup 0)
+ (match_operand:SI 1 "arith_operand" "L,r")))
+ (set (reg:SI 18)
+ (eq:SI (match_dup 0) (const_int 0)))]
+ "dead_or_set_p (insn, operands[0])"
+ "tst %1,%0 !t5")
+
+(define_peephole
+ [(set (match_operand:SI 0 "arith_reg_operand" "z,r")
+ (and:SI (match_dup 0)
+ (match_operand:SI 1 "arith_operand" "L,r")))
+ (set (reg:SI 18)
+ (eq:SI (match_dup 0) (const_int 0)))]
+ "dead_or_set_p (insn, operands[0])"
+ "tst %1,%0 !t4")
+
+;; -------------------------------------------------------------------------
+;; Combine patterns
+;; -------------------------------------------------------------------------
+
+(define_insn ""
+ [(set (match_operand:SI 0 "register_operand" "=r")
+ (plus:SI (match_operand:HI 1 "register_operand" "%0")
+ (match_operand:SI 2 "register_operand" "r")))]
+ ""
+ "add %2,%0 ! why")
+
+(define_insn "addc_2"
+ [(set (match_operand:SI 0 "arith_reg_operand" "=&r")
+ (plus:SI (reg:SI 18)
+ (match_operand:SI 1 "arith_reg_operand" "r")))
+ (clobber (reg:SI 18))]
+ ""
+ "mov #0,%0\;addc %1,%0 ! addc1"
+ [(set_attr "length" "4")])
+
+(define_insn "combine_1"
+ [(set (match_operand:SI 0 "arith_reg_operand" "=r")
+ (sign_extend:SI (mem:QI (match_operand:SI 1 "arith_reg_operand" "r"))))]
+ ""
+ "mov.b @%1,%0 ! why"
+ [(set_attr "type" "load")])
+
+(define_insn "combine_2"
+ [(set (reg:SI 18)
+ (eq (and:SI (match_operand:SI 0 "arith_reg_operand" "z,r")
+ (match_operand:SI 1 "arith_operand" "L,r"))
+ (const_int 0)))]
+ ""
+ "tst %1,%0 !t2")
+
+(define_split
+ [(set (pc)
+ (if_then_else
+ (match_operator 2 "equality_operator" [(match_operand:SI 0 "arith_reg_operand" "r")
+ (const_int 0)])
+ (label_ref (match_operand 1 "" ""))
+ (pc)))
+ (clobber (reg:SI 18))]
+ ""
+ [(set (reg:SI 18) (eq (and:SI (match_dup 0) (match_dup 0))
+ (const_int 0)))
+ (set (pc)
+ (if_then_else (match_op_dup 2 [(reg:SI 18) (const_int 1)])
+ (label_ref (match_dup 1))
+ (pc)))]
+ "")
+
+;; -------------------------------------------------------------------------
+;; Instructions to cope with inline literal tables
+;; -------------------------------------------------------------------------
+
+; 2 byte integer in line
+(define_insn "consttable_2"
+ [(unspec_volatile [(match_operand:SI 0 "general_operand" "=g")] 2)]
+ ""
+ "*
+{
+ assemble_integer (operands[0], 2, 1);
+ return \"\";
+}"
+ [(set_attr "length" "2")
+ (set_attr "in_delay_slot" "no")])
+
+; 4 byte integer in line
+
+(define_insn "consttable_4"
+ [(unspec_volatile [(match_operand:SI 0 "general_operand" "=g")] 4)]
+ ""
+ "*
+{
+ assemble_integer (operands[0], 4, 1);
+ return \"\";
+}"
+ [(set_attr "length" "4")
+ (set_attr "in_delay_slot" "no")])
+
+; 8 byte integer in line
+
+(define_insn "consttable_8"
+ [(unspec_volatile [(match_operand:SI 0 "general_operand" "=g")] 6)]
+ ""
+ "*
+{
+ assemble_integer (operands[0], 8, 1);
+ return \"\";
+}"
+ [(set_attr "length" "8")
+ (set_attr "in_delay_slot" "no")])
+
+; align to a two byte boundary
+
+(define_insn "align_2"
+ [(unspec_volatile [(const_int 0)] 10)]
+ ""
+ ".align 1"
+ [(set_attr "length" "0")
+ (set_attr "in_delay_slot" "no")])
+
+; align to a four byte boundary
+
+(define_insn "align_4"
+ [(unspec_volatile [(const_int 0)] 5)]
+ ""
+ ".align 2"
+ [(set_attr "in_delay_slot" "no")])
+
+; emitted at the end of the literal table, used to emit the
+; 32bit branch labels if needed.
+
+(define_insn "consttable_end"
+ [(unspec_volatile [(const_int 0)] 11)]
+ ""
+ "* return output_jump_label_table ();"
+ [(set_attr "in_delay_slot" "no")])
+
+
+;(define_split
+; [(set (subreg:SI (match_operand:QI 0 "register_operand" "=r") 0)
+; (plus:SI (subreg:SI (match_operand:QI 1 "general_operand" "g") 0)
+; (subreg:SI (match_operand:QI 2 "general_operand" "g") 0)))]
+; ""
+; [(set (match_dup 3) (plus:SI (match_dup 1) (match_dup 2)))
+; (set (match_dup 1) (subreg:SI (match_dup 3) 0))]
+; "operands[3] = gen_reg_rtx(SImode);")
+
+
+; byte arithmetic involving constants which need to be sign extended can be
+; fixed up...
+
+
+(define_split
+ [(set (subreg:SI (match_operand:QI 0 "register_operand" "=r") 0)
+ (plus:SI (subreg:SI (match_operand:QI 1 "register_operand" "0") 0)
+ (subreg:SI (match_operand 2 "immediate_operand" "n") 0)))]
+ ""
+ [(set (match_dup 4) (plus:SI (match_dup 2) (match_dup 3)))
+ (set (match_dup 0) (and:SI (match_dup 0) (const_int 255)))]
+ "{ int i = INTVAL(operands[2]) & 0xff;
+ if (i > 127) i = i - 256;
+ operands[3] = GEN_INT(i);
+ operands[4] = gen_reg_rtx(SImode);} ")
+
+
+(define_insn "movsi_k"
+ [(set (match_operand:SI 0 "register_operand" "=r")
+ (match_operand:SI 1 "immediate_operand" ""))]
+ "!pnum_clobbers"
+ "! this is a fake")
+
+
+(define_insn "movhi_k"
+ [(set (match_operand:HI 0 "register_operand" "=r")
+ (match_operand:HI 1 "immediate_operand" ""))]
+ "!pnum_clobbers"
+ "! this is a fake")
+
+(define_insn "movdi_k"
+ [(set (match_operand:DI 0 "register_operand" "=r")
+ (match_operand:DI 1 "immediate_operand" ""))]
+ "!pnum_clobbers"
+ "! this is a fake")
+
+;; -------------------------------------------------------------------------
+;; Misc
+;; -------------------------------------------------------------------------
+
+
+;; String/block move insn.
+
+(define_expand "movstrsi"
+ [(parallel [(set (mem:BLK (match_operand:BLK 0 "" ""))
+ (mem:BLK (match_operand:BLK 1 "" "")))
+ (use (match_operand:SI 2 "nonmemory_operand" ""))
+ (use (match_operand:SI 3 "immediate_operand" ""))
+ (clobber (reg:SI 17))
+ (clobber (reg:SI 4))
+ (clobber (reg:SI 5))
+ (clobber (reg:SI 0))])]
+ ""
+ "
+{
+ if(expand_block_move (operands))
+ DONE;
+ else FAIL;
+}")
+
+(define_insn "block_move_real"
+ [(parallel [(set (mem:BLK (reg:SI 4))
+ (mem:BLK (reg:SI 5)))
+ (use (match_operand:SI 0 "arith_reg_operand" "r"))
+ (clobber (reg:SI 17))
+ (clobber (reg:SI 4))
+ (clobber (reg:SI 5))
+ (clobber (reg:SI 0))])]
+ ""
+ "jsr @%0%#"
+ [(set_attr "length" "4")
+ (set_attr "type" "sfunc")
+ (set_attr "needs_delay_slot" "yes")])
+
+(define_insn "block_lump_real"
+ [(parallel [(set (mem:BLK (reg:SI 4))
+ (mem:BLK (reg:SI 5)))
+ (use (match_operand:SI 0 "arith_reg_operand" "r"))
+ (use (reg:SI 6))
+ (clobber (reg:SI 17))
+ (clobber (reg:SI 4))
+ (clobber (reg:SI 5))
+ (clobber (reg:SI 6))
+ (clobber (reg:SI 0))])]
+ ""
+ "jsr @%0%#"
+ [(set_attr "length" "4")
+ (set_attr "type" "sfunc")
+ (set_attr "needs_delay_slot" "yes")])