summaryrefslogtreecommitdiff
path: root/gcc/config
diff options
context:
space:
mode:
authordje <dje@138bc75d-0d04-0410-961f-82ee72b054a4>1994-07-11 22:51:17 +0000
committerdje <dje@138bc75d-0d04-0410-961f-82ee72b054a4>1994-07-11 22:51:17 +0000
commitb839e0b4e0cf8e87b0b1ee93fa0edea8d1352acb (patch)
tree3394fc6112d3885681f8c8ef122478aeb8bc512c /gcc/config
parentd22d451ba2f18fc51603861935e052b69b4d791b (diff)
downloadgcc-b839e0b4e0cf8e87b0b1ee93fa0edea8d1352acb.tar.gz
* h8300/h8300.c (cpu_type, names_extended, names_upper_extended,
h8_reg_names, h8_push_ops, h8_pop_ops, h8_mov_ops, h8_push_op, h8_pop_op, h8_mov_op, current_function_anonymous_args, extra_pop, hand_list): New variables. (h8300_init_once, asm_file_start, asm_file_end, ok_for_bclr, o_operand, p_operand, call_insn_operand, jump_address_operand, bit_operand, eq_operator, const_costs, notice_update_cc, bit_operator, nshift_operator, expand_a_shift, get_shift_alg, emit_a_shift, fix_bit_operand): New functions. (shift_alg, shift_type, shift_mode): New enums. (shift_insn): New struct. (shift_n_bits, can_shift): Deleted. (shift_one, rotate_one): New variables. (WORD_REG_USED): New macro (was function word_reg_used). (dosize, function_prologue, function_epilogue, print_operand_address): Add h8/300h support. (small_power_of_two): Renamed from potl8. (potg8): Deleted. (general_operand_src): Fix POST_INC case. (general_operand_dst): Fix PRE_DEC case. (function_arg): 3 regs of args are passed if -mquickcall. 4 regs of args are passed to handwritten assembler routines. (print_operand): New cases 'A', 'P', 'S', 'U', 'W', 'b', 'c', 'd', 'g'. Delete case 'O'. Sort cases. Add h8/300h support. (do_movsi): Renamed from domovsi. Handle reload_in_progress and reload_completed. (initial_offset): Renamed from io. Add h8/300h support. git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@7729 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'gcc/config')
-rw-r--r--gcc/config/h8300/h8300.c1848
1 files changed, 1484 insertions, 364 deletions
diff --git a/gcc/config/h8300/h8300.c b/gcc/config/h8300/h8300.c
index 26f630efbd9..b559cef5b4d 100644
--- a/gcc/config/h8300/h8300.c
+++ b/gcc/config/h8300/h8300.c
@@ -1,8 +1,7 @@
/* Subroutines for insn-output.c for Hitachi H8/300.
- Copyright (C) 1992,1993 Free Software Foundation, Inc.
-
- Contributed by Steve Chamberlain (sac@cygnus.com) and
- Jim Wilson (wilson@cygnus.com).
+ Copyright (C) 1992, 1993, 1994 Free Software Foundation, Inc.
+ Contributed by Steve Chamberlain (sac@cygnus.com),
+ Jim Wilson (wilson@cygnus.com), and Doug Evans (dje@cygnus.com).
This file is part of GNU CC.
@@ -40,42 +39,85 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
void print_operand_address ();
char *index ();
+/* CPU_TYPE, says what cpu we're compiling for. */
+int cpu_type;
+
/* True if a #pragma interrupt has been seen for the current function. */
int pragma_interrupt;
/* True if a #pragma saveall has been seen for the current function. */
int pragma_saveall;
-char *names_big[]
- = {"r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7"};
+static char *names_big[] =
+{"r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7"};
+
+static char *names_extended[] =
+{"er0", "er1", "er2", "er3", "er4", "er5", "er6", "er7"};
+
+static char *names_upper_extended[] =
+{"e0", "e1", "e2", "e3", "e4", "e5", "e6", "e7"};
+
+/* Points to one of the above. */
+/* ??? The above could be put in an array indexed by CPU_TYPE. */
+char **h8_reg_names;
+
+/* Various operations needed by the following, indexed by CPU_TYPE. */
+/* ??? The h8/300 assembler doesn't understand pop.w (yet). */
+
+static char *h8_push_ops[2] =
+{"push", "push.l"};
+static char *h8_pop_ops[2] =
+{"pop", "pop.l"};
+static char *h8_mov_ops[2] =
+{"mov.w", "mov.l"};
+
+char *h8_push_op, *h8_pop_op, *h8_mov_op;
+
+/* Initialize various cpu specific globals at start up. */
+
+void
+h8300_init_once ()
+{
+ if (TARGET_H8300)
+ {
+ cpu_type = (int) CPU_H8300;
+ h8_reg_names = names_big;
+ }
+ else
+ {
+ cpu_type = (int) CPU_H8300H;
+ h8_reg_names = names_extended;
+ }
+ h8_push_op = h8_push_ops[cpu_type];
+ h8_pop_op = h8_pop_ops[cpu_type];
+ h8_mov_op = h8_mov_ops[cpu_type];
+}
char *
byte_reg (x, b)
rtx x;
int b;
{
- static char *names_small[]
- = {"r0l", "r0h", "r1l", "r1h", "r2l", "r2h", "r3l", "r3h",
- "r4l", "r4h", "r5l", "r5h", "r6l", "r6h", "r7lBAD", "r7hBAD"};
+ static char *names_small[] =
+ {"r0l", "r0h", "r1l", "r1h", "r2l", "r2h", "r3l", "r3h",
+ "r4l", "r4h", "r5l", "r5h", "r6l", "r6h", "r7lBAD", "r7hBAD"};
return names_small[REGNO (x) * 2 + b];
}
/* REGNO must be saved/restored across calls if this macro is true. */
-static int
-word_reg_used (regno)
-int regno;
-{
- if (regno < 7
- && (pragma_interrupt || pragma_saveall
- || (regno == FRAME_POINTER_REGNUM && regs_ever_live[regno])
- || (regs_ever_live[regno] & ! call_used_regs[regno])))
- return 1;
- return 0;
-}
+
+#define WORD_REG_USED(regno) \
+ (regno < 7 && \
+ (pragma_interrupt \
+ || pragma_saveall \
+ || (regno == FRAME_POINTER_REGNUM && regs_ever_live[regno]) \
+ || (regs_ever_live[regno] & !call_used_regs[regno])))
/* Output assembly language to FILE for the operation OP with operand size
- SIZE. */
+ SIZE to adjust the stack pointer. */
+/* ??? FPED is currently unused. */
+
static void
dosize (file, op, size, fped)
FILE *file;
@@ -86,6 +128,7 @@ dosize (file, op, size, fped)
switch (size)
{
case 4:
+ /* ??? TARGET_H8300H can do this in one insn. */
case 3:
fprintf (file, "\t%ss\t#%d,sp\n", op, 2);
size -= 2;
@@ -98,66 +141,112 @@ dosize (file, op, size, fped)
case 0:
break;
default:
- fprintf (file, "\tmov.w\t#%d,r5\n\t%s.w\tr5,sp\n", size, op);
+ if (TARGET_H8300)
+ fprintf (file, "\tmov.w\t#%d,r3\n\t%s.w\tr3,sp\n", size, op);
+ else
+ fprintf (file, "\t%s\t#%d,sp\n", op, size);
size = 0;
break;
}
}
/* Output assembly language code for the function prologue. */
-static int push_order[FIRST_PSEUDO_REGISTER]
- = {6, 5, 4, 3, 2, 1, 0, -1, -1};
-static int pop_order[FIRST_PSEUDO_REGISTER]
- = {0, 1, 2, 3, 4, 5, 6, -1, -1};
+static int push_order[FIRST_PSEUDO_REGISTER] =
+{6, 5, 4, 3, 2, 1, 0, -1, -1};
+static int pop_order[FIRST_PSEUDO_REGISTER] =
+{0, 1, 2, 3, 4, 5, 6, -1, -1};
/* This is what the stack looks like after the prolog of
a function with a frame has been set up:
- <pushed args>
- return pc
- fp-> old fp
- <locals>
- <saved register-0>
- <saved register-1>
- sp-> <saved register-n>
-
+ <args>
+ PC
+ FP <- fp
+ <locals>
+ <saved registers> <- sp
This is what the stack looks like after the prolog of
a function which doesn't have a frame:
- <pushed args>
- return pc
- <locals>
- <saved register-0>
- sp-> <saved register-n>
+ <args>
+ PC
+ <locals>
+ <saved registers> <- sp
*/
+int current_function_anonymous_args;
+
+/* Extra arguments to pop, in words (IE: 2 bytes for 300, 4 for 300h */
+static int extra_pop;
+
void
function_prologue (file, size)
FILE *file;
int size;
{
register int mask = 0;
- int fsize = (size + 1) & -2;
+ int fsize = (size + STACK_BOUNDARY / 8 - 1) & -STACK_BOUNDARY / 8;
int idx;
+ extra_pop = 0;
- if (frame_pointer_needed)
+ if (current_function_anonymous_args && TARGET_QUICKCALL)
{
- /* Push the fp. */
- fprintf (file, "\tpush\t%s\n", names_big[FRAME_POINTER_REGNUM]);
- fprintf (file, "\tmov.w\tr7,r6\n");
+ /* Push regs as if done by caller, and move around return address. */
- /* Leave room for the locals. */
+ switch (current_function_args_info.nbytes / UNITS_PER_WORD)
+ {
+ case 0:
+ /* get ret addr */
+ fprintf (file, "\t%s\t%s\n", h8_pop_op, h8_reg_names[3]);
+ fprintf (file, "\t%s\t%s\n", h8_push_op, h8_reg_names[2]);
+ fprintf (file, "\t%s\t%s\n", h8_push_op, h8_reg_names[1]);
+ fprintf (file, "\t%s\t%s\n", h8_push_op, h8_reg_names[0]);
+ /* push it again */
+ fprintf (file, "\t%s\t%s\n", h8_push_op, h8_reg_names[3]);
+ extra_pop = 3;
+ break;
+ case 1:
+ /* get ret addr */
+ fprintf (file, "\t%s\t%s\n", h8_pop_op, h8_reg_names[3]);
+ fprintf (file, "\t%s\t%s\n", h8_push_op, h8_reg_names[2]);
+ fprintf (file, "\t%s\t%s\n", h8_push_op, h8_reg_names[1]);
+ /* push it again */
+ fprintf (file, "\t%s\t%s\n", h8_push_op, h8_reg_names[3]);
+ extra_pop = 2;
+ break;
+ case 2:
+ /* get ret addr */
+ fprintf (file, "\t%s\t%s\n", h8_pop_op, h8_reg_names[3]);
+ fprintf (file, "\t%s\t%s\n", h8_push_op, h8_reg_names[2]);
+ /* push it again */
+ fprintf (file, "\t%s\t%s\n", h8_push_op, h8_reg_names[3]);
+ extra_pop = 1;
+ break;
+ default:
+ fprintf (file, "; varargs\n");
+ break;
+ }
+ }
+
+ if (frame_pointer_needed)
+ {
+ /* Push fp */
+ fprintf (file, "\t%s\t%s\n", h8_push_op,
+ h8_reg_names[FRAME_POINTER_REGNUM]);
+ fprintf (file, "\t%s\t%s,%s\n", h8_mov_op,
+ h8_reg_names[STACK_POINTER_REGNUM],
+ h8_reg_names[FRAME_POINTER_REGNUM]);
+
+ /* leave room for locals */
dosize (file, "sub", fsize, 1);
- /* Push the rest of the registers. */
- for (idx = 0; idx < FIRST_PSEUDO_REGISTER; idx++)
+ /* Push the rest of the registers */
+ for (idx = 0; idx < FIRST_PSEUDO_REGISTER; idx++)
{
int regno = push_order[idx];
- if (regno >= 0 && word_reg_used (regno)
- && regno != FRAME_POINTER_REGNUM)
- fprintf (file, "\tpush\t%s\n", names_big[regno]);
+ if (regno >= 0 && WORD_REG_USED (regno) && regno != FRAME_POINTER_REGNUM)
+ fprintf (file, "\t%s\t%s\n", h8_push_op, h8_reg_names[regno]);
}
}
else
@@ -167,8 +256,8 @@ function_prologue (file, size)
{
int regno = push_order[idx];
- if (regno >= 0 && word_reg_used (regno))
- fprintf (file, "\tpush\t%s\n", names_big[regno]);
+ if (regno >= 0 && WORD_REG_USED (regno))
+ fprintf (file, "\t%s\t%s\n", h8_push_op, h8_reg_names[regno]);
}
}
}
@@ -182,7 +271,7 @@ function_epilogue (file, size)
{
register int regno;
register int mask = 0;
- int fsize = (size + 1) & -2;
+ int fsize = (size + STACK_BOUNDARY / 8 - 1) & -STACK_BOUNDARY / 8;
int nregs;
int offset;
int idx;
@@ -198,43 +287,87 @@ function_epilogue (file, size)
if (frame_pointer_needed)
{
- /* Pop saved registers. */
+ /* Pop saved registers */
for (idx = 0; idx < FIRST_PSEUDO_REGISTER; idx++)
{
regno = pop_order[idx];
- if (regno >= 0 && regno != FRAME_POINTER_REGNUM
- && word_reg_used (regno))
- fprintf (file, "\tpop\t%s\n", names_big[regno]);
+ if (regno >= 0 && regno != FRAME_POINTER_REGNUM && WORD_REG_USED (regno))
+ fprintf (file, "\t%s\t%s\n", h8_pop_op, h8_reg_names[regno]);
}
- /* Deallocate locals. */
+ /* deallocate locals */
dosize (file, "add", fsize, 1);
- /* Pop frame pointer. */
- fprintf (file, "\tpop\t%s\n", names_big[FRAME_POINTER_REGNUM]);
+ /* pop frame pointer */
+ fprintf (file, "\t%s\t%s\n", h8_pop_op, h8_reg_names[FRAME_POINTER_REGNUM]);
}
else
{
- /* Deallocate locals and pop saved registers. */
+ /* pop saved registers */
for (idx = 0; idx < FIRST_PSEUDO_REGISTER; idx++)
{
regno = pop_order[idx];
- if (regno >= 0 && word_reg_used (regno))
- fprintf (file, "\tpop\t%s\n", names_big[regno]);
+ if (regno >= 0 && WORD_REG_USED (regno))
+ fprintf (file, "\t%s\t%s\n", h8_pop_op, h8_reg_names[regno]);
}
+ /* deallocate locals */
dosize (file, "add", fsize, 0);
}
- if (pragma_interrupt)
- fprintf (file, "\trte\n");
+
+ if (extra_pop)
+ {
+ fprintf (file, "\t%s\t%s\n", h8_pop_op, h8_reg_names[3]);
+ while (extra_pop)
+ {
+ fprintf (file, "\t%s\t%s\n", h8_pop_op, h8_reg_names[2]);
+ extra_pop--;
+ }
+ fprintf (file, "\tjmp @%s\n", h8_reg_names[3]);
+ }
else
- fprintf (file, "\trts\n");
+ {
+ if (pragma_interrupt)
+ fprintf (file, "\trte\n");
+ else
+ fprintf (file, "\trts\n");
+ }
pragma_interrupt = 0;
pragma_saveall = 0;
+
+ current_function_anonymous_args = 0;
+}
+
+/* Output assembly code for the start of the file. */
+
+asm_file_start (file)
+ FILE *file;
+{
+ fprintf (file, ";\tGCC For the Hitachi H8/300\n");
+ fprintf (file, ";\tBy Hitachi America Ltd and Cygnus Support\n");
+ fprintf (file, ";\trelease F-1\n");
+ if (optimize)
+ fprintf (file, "; -O%d\n", optimize);
+ if (TARGET_H8300H)
+ fprintf (file, "\n\t.h8300h\n");
+ else
+ fprintf (file, "\n\n");
+ output_file_directive (file, main_input_filename);
+}
+
+/* Output assembly language code for the end of file. */
+
+void
+asm_file_end (file)
+ FILE *file;
+{
+ fprintf (file, "\t.end\n");
}
-/* Return true if VALUE is a valid constant for constraint 'P'. */
+/* Return true if VALUE is a valid constant for constraint 'P'.
+ IE: VALUE is a power of two <= 2**15. */
int
-potl8 (value)
+small_power_of_two (value)
+ int value;
{
switch (value)
{
@@ -246,18 +379,6 @@ potl8 (value)
case 32:
case 64:
case 128:
- return 1;
- }
- return 0;
-}
-
-/* Return true if VALUE is a valid constant for constraint 'O'. */
-int
-potg8 (value)
- int value;
-{
- switch (value)
- {
case 256:
case 512:
case 1024:
@@ -271,48 +392,167 @@ potg8 (value)
return 0;
}
+/* Return true if VALUE is a valid constant for constraint 'O', which
+ means that the constant would be ok to use as a bit for a bclr
+ instruction. */
+
+int
+ok_for_bclr (value)
+ int value;
+{
+ return small_power_of_two ((~value) & 0xff);
+}
+
/* Return true is OP is a valid source operand for an integer move
instruction. */
+
int
general_operand_src (op, mode)
rtx op;
enum machine_mode mode;
{
- /* We can't have a pre-dec as a source. */
- if (GET_CODE (op) == MEM && GET_CODE (XEXP (op, 0)) == PRE_DEC)
- return 0;
+ if (GET_CODE (op) == MEM && GET_CODE (XEXP (op, 0)) == POST_INC)
+ return 1;
return general_operand (op, mode);
}
/* Return true if OP is a valid destination operand for an integer move
instruction. */
+
int
general_operand_dst (op, mode)
rtx op;
enum machine_mode mode;
{
- /* We can't have a post-inc as a dest. */
- if (GET_CODE (op) == MEM && GET_CODE (XEXP (op, 0)) == POST_INC)
- return 0;
+ if (GET_CODE (op) == MEM && GET_CODE (XEXP (op, 0)) == PRE_DEC)
+ return 1;
return general_operand (op, mode);
}
-
+
+/* Return true if OP is a const valid for a bit clear instruction. */
+
+int
+o_operand (operand, mode)
+ rtx operand;
+ enum machine_mode mode;
+{
+ return (GET_CODE (operand) == CONST_INT
+ && CONST_OK_FOR_O (INTVAL (operand)));
+}
+
+/* Return true if OP is a const valid for a bit set or bit xor instruction. */
+
+int
+p_operand (operand, mode)
+ rtx operand;
+ enum machine_mode mode;
+{
+ return (GET_CODE (operand) == CONST_INT
+ && CONST_OK_FOR_P (INTVAL (operand)));
+}
+
+/* Return true if OP is a valid call operand. */
+
+int
+call_insn_operand (op, mode)
+ rtx op;
+ enum machine_mode mode;
+{
+ if (GET_CODE (op) == MEM)
+ {
+ rtx inside = XEXP (op, 0);
+ if (register_operand (inside, Pmode))
+ return 1;
+ if (CONSTANT_ADDRESS_P (inside))
+ return 1;
+ }
+ return 0;
+}
+
+/* Return true if OP is a valid jump operand. */
+
+int
+jump_address_operand (op, mode)
+ rtx op;
+ enum machine_mode mode;
+{
+ if (GET_CODE (op) == REG)
+ return mode == Pmode;
+
+ if (GET_CODE (op) == MEM)
+ {
+ rtx inside = XEXP (op, 0);
+ if (register_operand (inside, Pmode))
+ return 1;
+ if (CONSTANT_ADDRESS_P (inside))
+ return 1;
+ }
+ return 0;
+}
+
+/* Recognize valid operands for bitfield instructions. */
+
+extern int rtx_equal_function_value_matters;
+
+int
+bit_operand (op, mode)
+ rtx op;
+ enum machine_mode mode;
+{
+ /* We can except any general operand, expept that MEM operands must
+ be limited to those that use addresses valid for the 'U' constraint. */
+ if (!general_operand (op, mode))
+ return 0;
+
+ /* Accept any mem during RTL generation. Otherwise, the code that does
+ insv and extzv will think that we can not handle memory. However,
+ to avoid reload problems, we only accept 'U' MEM operands after RTL
+ generation. This means that any named pattern which uses this predicate
+ must force its operands to match 'U' before emitting RTL. */
+
+ if (GET_CODE (op) == REG)
+ return 1;
+ if (GET_CODE (op) == SUBREG)
+ return 1;
+ if (!rtx_equal_function_value_matters)
+ {
+ /* We're building rtl */
+ return GET_CODE (op) == MEM;
+ }
+ else
+ {
+ return (GET_CODE (op) == MEM
+ && EXTRA_CONSTRAINT (op, 'U'));
+ }
+}
+
+/* Recognize valid operators for bit test. */
+
+int
+eq_operator (x, mode)
+ rtx x;
+ enum machine_mode mode;
+{
+ return (GET_CODE (x) == EQ || GET_CODE (x) == NE);
+}
+
/* Handle machine specific pragmas for compatibility with existing
- compilers for the H8/300
+ compilers for the H8/300.
pragma saveall generates prolog/epilog code which saves and
restores all the registers on function entry.
-
+
pragma interrupt saves and restores all registers, and exits with
an rte instruction rather than an rts. A pointer to a function
with this attribute may be safely used in an interrupt vector. */
+
int
handle_pragma (file)
FILE *file;
{
int c;
char pbuf[20];
- int psize;
+ int psize = 0;
c = getc (file);
while (c == ' ' || c == '\t')
@@ -321,12 +561,13 @@ handle_pragma (file)
if (c == '\n' || c == EOF)
return c;
- for (psize = 0; psize < sizeof (pbuf) - 1 && isalpha (c); psize++)
+ /* The only pragmas we understand are interrupt and saveall. */
+ while (psize < sizeof (pbuf) - 1
+ && isalpha (c))
{
- pbuf[psize] = c;
+ pbuf[psize++] = c;
c = getc (file);
}
-
pbuf[psize] = 0;
if (strcmp (pbuf, "interrupt") == 0)
@@ -335,6 +576,22 @@ handle_pragma (file)
if (strcmp (pbuf, "saveall") == 0)
pragma_saveall = 1;
+ /* ??? This is deprecated. Use section attributes. */
+ if (strcmp (pbuf, "section") == 0)
+ {
+ while (c && !isalpha (c))
+ c = getc (file);
+ psize = 0;
+ while (psize < sizeof (pbuf) - 1
+ && isalpha (c) || isdigit (c) || c == '_')
+ {
+ pbuf[psize++] = c;
+ c = getc (file);
+ }
+ pbuf[psize] = 0;
+ named_section (pbuf);
+ }
+ ungetc (c, file);
return c;
}
@@ -342,6 +599,29 @@ handle_pragma (file)
the rtx to represent where it is passed. CUM represents the state after
the last argument. NAMED is not used. */
+static char *hand_list[] =
+{
+ "__main",
+ "__cmpsi2",
+ "__divhi3",
+ "__modhi3",
+ "__udivhi3",
+ "__umodhi3",
+ "__divsi3",
+ "__modsi3",
+ "__udivsi3",
+ "__umodsi3",
+ "__mulhi3",
+ "__mulsi3",
+ "__reg_memcpy",
+ "__reg_memset",
+ "__ucmpsi2",
+ 0,
+};
+
+/* Return an RTX to represent where a value with mode MODE will be returned
+ from a function. If the result is 0, the argument is pushed. */
+
rtx
function_arg (cum, mode, type, named)
CUMULATIVE_ARGS *cum;
@@ -350,83 +630,154 @@ function_arg (cum, mode, type, named)
int named;
{
rtx result = 0;
- int libcall = 0;
+ char *fname;
+ int regpass = 0;
- /* Right now reload has a problem with reg passing with small reg
- classes. */
+ /* Pass 3 regs worth of data in regs when user asked on the command line. */
+ if (TARGET_QUICKCALL)
+ regpass = 3;
- if (cum->libcall || (named && TARGET_QUICKCALL))
- libcall = 1;
+ /* If calling hand written assembler, use 4 regs of args. */
- if (TARGET_NOQUICK)
- libcall = 0;
+ if (cum->libcall)
+ {
+ char **p;
+
+ fname = XSTR (cum->libcall, 0);
+
+ /* See if this libcall is one of the hand coded ones. */
- if (mode != VOIDmode && libcall && mode != DFmode && mode != SFmode)
+ for (p = hand_list; *p && strcmp (*p, fname) != 0; p++)
+ ;
+
+ if (*p)
+ regpass = 4;
+ }
+
+ if (regpass)
{
- switch (cum->nbytes)
+ int size;
+
+ if (mode == BLKmode)
+ size = int_size_in_bytes (type);
+ else
+ size = GET_MODE_SIZE (mode);
+
+ if (size + cum->nbytes > regpass * UNITS_PER_WORD)
+ {
+ result = 0;
+ }
+ else
+ {
+ switch (cum->nbytes / UNITS_PER_WORD)
+ {
+ case 0:
+ result = gen_rtx (REG, mode, 0);
+ break;
+ case 1:
+ result = gen_rtx (REG, mode, 1);
+ break;
+ case 2:
+ result = gen_rtx (REG, mode, 2);
+ break;
+ case 3:
+ result = gen_rtx (REG, mode, 3);
+ break;
+ default:
+ result = 0;
+ }
+ }
+ }
+
+ return result;
+}
+
+/* Return the cost of the rtx R with code CODE. */
+
+int
+const_costs (r, c)
+ rtx r;
+ enum rtx_code c;
+{
+ switch (c)
+ {
+ case CONST_INT:
+ switch (INTVAL (r))
{
case 0:
- result = gen_rtx (REG, mode, 0);
- break;
+ case 1:
case 2:
- result = gen_rtx (REG, mode, 1);
- break;
- case 4:
- result = gen_rtx (REG, mode, 4);
- break;
- case 6:
- result = gen_rtx (REG, mode, 5);
- break;
- default:
+ case -1:
+ case -2:
return 0;
+ default:
+ return 1;
}
+
+ case CONST:
+ case LABEL_REF:
+ case SYMBOL_REF:
+ return 3;
+
+ case CONST_DOUBLE:
+ return 20;
+
+ default:
+ return 4;
}
- return result;
}
-
+
/* Documentation for the machine specific operand escapes:
+ 'A' print rn in h8/300 mode, erN in H8/300H mode
'C' print (operand - 2).
- 'E' low byte of reg or -ve lsb of constant
- 'F' high byte of reg of -ve msb of constant
-
- 'G' negate constant
+ 'E' like s but negative.
+ 'F' like t but negative.
+ 'G' constant just the negative
'L' fake label, changed after used twice.
'M' turn a 'M' constant into its negative mod 2.
+ 'P' if operand is incing/decing sp, print .w, otherwise .b.
+ 'S' print operand as a long word
'T' print operand as a word
- 'V' print log2 of constant - used for bset instructions
- 'X' 8 bit register or other operand
-
+ 'U' if operand is incing/decing sp, print l, otherwise nothing.
+ 'V' find the set bit, and print its number.
+ 'W' find the clear bit, and print its number.
+ 'X' print operand as a byte
'Y' print either l or h depending on whether last 'Z' operand < 8 or >= 8.
- 'Z' print int & 7
-
- 'e' first word of 32 bit value
- 'f' second word of 32 bit value
-
+ 'Z' print int & 7.
+ 'b' print the bit opcode
+ 'c' print the ibit opcode
+ 'd' bcc if EQ, bcs if NE
+ 'e' first word of 32 bit value - if reg, then least reg. if mem
+ then least. if const then most sig word
+ 'f' second word of 32 bit value - if reg, then biggest reg. if mem
+ then +2. if const then least sig word
+ 'g' bcs if EQ, bcc if NE
'j' print operand as condition code.
'k' print operand as reverse condition code.
-
- 's' low byte of 16 bit value
- 't' high byte of 16 bit value
-
- 'w' 1st byte of 32 bit value zzzzzzzz yyyyyyyy xxxxxxxx wwwwwwww
- 'x' 2nd byte of 32 bit value
- 'y' 3rd byte of 32 bit value
- 'z' 4th byte of 32 bit value
-
- */
+ 's' print as low byte of 16 bit value
+ 't' print as high byte of 16 bit value
+ 'w' print as low byte of 32 bit value
+ 'x' print as 2nd byte of 32 bit value
+ 'y' print as 3rd byte of 32 bit value
+ 'z' print as msb of 32 bit value
+*/
/* Return assembly language string which identifies a comparison type. */
-char *
+static char *
cond_string (code)
enum rtx_code code;
{
switch (code)
{
case NE:
+ if (cc_prev_status.flags & CC_DONE_CBIT)
+ return "cs";
return "ne";
case EQ:
+ if (cc_prev_status.flags & CC_DONE_CBIT)
+ return "cc";
return "eq";
case GE:
return "ge";
@@ -465,149 +816,184 @@ print_operand (file, x, code)
static char *last_p;
/* This is used for communication between the 'Z' and 'Y' codes. */
+ /* ??? 'V' and 'W' use it too. */
static int bitint;
switch (code)
{
- case 'L':
- /* 'L' must always be used twice in a single pattern. It generates
- the same lable twice, and then will generate a unique label the
- next time it is used. */
- asm_fprintf (file, "tl%d", (lab++) / 2);
- break;
-
- case 'X':
+ case 'A':
if (GET_CODE (x) == REG)
- fprintf (file, "%s", byte_reg (x, 0));
+ fprintf (file, "%s", h8_reg_names[REGNO (x)]);
else
goto def;
break;
-
+ case 'C':
+ fprintf (file, "#%d", INTVAL (x) - 2);
+ break;
+ case 'E':
+ switch (GET_CODE (x))
+ {
+ case REG:
+ fprintf (file, "%sl", names_big[REGNO (x)]);
+ break;
+ case CONST_INT:
+ fprintf (file, "#%d", (-INTVAL (x)) & 0xff);
+ break;
+ default:
+ abort ();
+ }
+ break;
+ case 'F':
+ switch (GET_CODE (x))
+ {
+ case REG:
+ fprintf (file, "%sh", names_big[REGNO (x)]);
+ break;
+ case CONST_INT:
+ fprintf (file, "#%d", ((-INTVAL (x)) & 0xff00) >> 8);
+ break;
+ default:
+ abort ();
+ }
+ break;
case 'G':
if (GET_CODE (x) != CONST_INT)
abort ();
fprintf (file, "#%d", 0xff & (-INTVAL (x)));
break;
-
- case 'T':
- if (GET_CODE (x) == REG)
- fprintf (file, "%s", names_big[REGNO (x)]);
- else
- goto def;
- break;
-
- case 'w':
- if (GET_CODE (x) == CONST_INT)
- fprintf (file, "#%d", INTVAL (x) & 0xff);
- else
- fprintf (file, "%s", byte_reg (x, 2));
+ case 'L':
+ /* 'L' must always be used twice in a single pattern. It generates
+ the same lable twice, and then will generate a unique label the
+ next time it is used. */
+ asm_fprintf (file, "tl%d", (lab++) / 2);
break;
-
- case 'x':
- if (GET_CODE (x) == CONST_INT)
- fprintf (file, "#%d", (INTVAL (x) >> 8) & 0xff);
- else
- fprintf (file, "%s", byte_reg (x, 3));
+ case 'M':
+ /* For 3/-3 and 4/-4, the other 2 is handled separately. */
+ switch (INTVAL (x))
+ {
+ case 2:
+ case 4:
+ case -2:
+ case -4:
+ fprintf (file, "#2");
+ break;
+ case 1:
+ case 3:
+ case -1:
+ case -3:
+ fprintf (file, "#1");
+ break;
+ default:
+ abort ();
+ }
break;
-
- case 'y':
- if (GET_CODE (x) == CONST_INT)
- fprintf (file, "#%d", (INTVAL (x) >> 16) & 0xff);
+ case 'P':
+ if (REGNO (XEXP (XEXP (x, 0), 0)) == STACK_POINTER_REGNUM)
+ {
+ last_p = "";
+ fprintf (file, ".w");
+ }
else
- fprintf (file, "%s", byte_reg (x, 0));
+ {
+ last_p = "l";
+ fprintf (file, ".b");
+ }
break;
-
- case 'z':
- if (GET_CODE (x) == CONST_INT)
- fprintf (file, "#%d", (INTVAL (x) >> 24) & 0xff);
+ case 'S':
+ if (GET_CODE (x) == REG)
+ fprintf (file, "%s", names_extended[REGNO (x)]);
else
- fprintf (file, "%s", byte_reg (x, 1));
+ goto def;
break;
-
- /* FOR 16 bits. */
- case 't':
- if (GET_CODE (x) == CONST_INT)
- fprintf (file, "#%d", (INTVAL (x) >> 8) & 0xff);
+ case 'T':
+ if (GET_CODE (x) == REG)
+ fprintf (file, "%s", names_big[REGNO (x)]);
else
- fprintf (file, "%s", byte_reg (x, 1));
+ goto def;
break;
-
- case 's':
- if (GET_CODE (x) == CONST_INT)
- fprintf (file, "#%d", (INTVAL (x)) & 0xff);
- else
- fprintf (file, "%s", byte_reg (x, 0));
+ case 'U':
+ fprintf (file, "%s%s", names_big[REGNO (x)], last_p);
break;
-
- case 'u':
- if (GET_CODE (x) != CONST_INT)
+ case 'V':
+ bitint = exact_log2 (INTVAL (x));
+ if (bitint == -1)
abort ();
- fprintf (file, "%d", INTVAL (x));
- break;
-
- case 'Z':
- bitint = INTVAL (x);
fprintf (file, "#%d", bitint & 7);
break;
-
- case 'Y':
- fprintf (file, "%c", bitint > 7 ? 'h' : 'l');
- break;
-
- case 'O':
+ case 'W':
bitint = exact_log2 ((~INTVAL (x)) & 0xff);
if (bitint == -1)
abort ();
fprintf (file, "#%d", bitint & 7);
break;
-
- case 'V':
- bitint = exact_log2 (INTVAL (x));
+ case 'X':
+ if (GET_CODE (x) == REG)
+ fprintf (file, "%s", byte_reg (x, 0));
+ else
+ goto def;
+ break;
+ case 'Y':
if (bitint == -1)
abort ();
+ if (GET_CODE (x) == REG)
+ fprintf (file, "%s%c", names_big[REGNO (x)], bitint > 7 ? 'h' : 'l');
+ else
+ print_operand (file, x, 0);
+ bitint = -1;
+ break;
+ case 'Z':
+ bitint = INTVAL (x);
fprintf (file, "#%d", bitint & 7);
break;
-
- case 'P':
- if (REGNO (XEXP (XEXP (x, 0), 0)) == STACK_POINTER_REGNUM)
+ case 'b':
+ switch (GET_CODE (x))
{
- last_p = "";
- fprintf (file, ".w");
+ case IOR:
+ fprintf (file, "bor");
+ break;
+ case XOR:
+ fprintf (file, "bxor");
+ break;
+ case AND:
+ fprintf (file, "band");
+ break;
}
- else
+ break;
+ case 'c':
+ switch (GET_CODE (x))
{
- last_p = "l";
- fprintf (file, ".b");
+ case IOR:
+ fprintf (file, "bior");
+ break;
+ case XOR:
+ fprintf (file, "bixor");
+ break;
+ case AND:
+ fprintf (file, "biand");
+ break;
}
break;
-
- case 'U':
- fprintf (file, "%s%s", names_big[REGNO (x)], last_p);
- break;
-
- case 'M':
- /* For -4 and -2, the other 2 is handled separately. */
- switch (INTVAL (x))
+ case 'd':
+ switch (GET_CODE (x))
{
- case -2:
- case -4:
- fprintf (file, "#2");
+ case EQ:
+ fprintf (file, "bcc");
break;
- case -1:
- case -3:
- fprintf (file, "#1");
+ case NE:
+ fprintf (file, "bcs");
break;
-
default:
abort ();
}
break;
-
case 'e':
switch (GET_CODE (x))
{
case REG:
- fprintf (file, "%s", names_big[REGNO (x)]);
+ if (TARGET_H8300)
+ fprintf (file, "%s", names_big[REGNO (x)]);
+ else
+ fprintf (file, "%s", names_upper_extended[REGNO (x)]);
break;
case MEM:
x = adj_offsettable_operand (x, 0);
@@ -621,77 +1007,110 @@ print_operand (file, x, code)
break;
}
break;
-
case 'f':
switch (GET_CODE (x))
{
case REG:
- fprintf (file, "%s", names_big[REGNO (x) + 1]);
+ if (TARGET_H8300)
+ fprintf (file, "%s", names_big[REGNO (x) + 1]);
+ else
+ fprintf (file, "%s", names_big[REGNO (x)]);
break;
-
case MEM:
x = adj_offsettable_operand (x, 2);
print_operand (file, x, 0);
break;
-
case CONST_INT:
fprintf (file, "#%d", INTVAL (x) & 0xffff);
break;
-
default:
abort ();
}
break;
-
- case 'C':
- fprintf (file, "#%d", INTVAL (x) - 2);
- break;
-
- case 'E':
- switch (GET_CODE (x))
- {
- case REG:
- fprintf (file, "%sl", names_big[REGNO (x)]);
- break;
-
- case CONST_INT:
- fprintf (file, "#%d", (-INTVAL (x)) & 0xff);
- break;
-
- default:
- abort ();
- }
- break;
-
- case 'F':
+ case 'g':
switch (GET_CODE (x))
{
- case REG:
- fprintf (file, "%sh", names_big[REGNO (x)]);
+ case NE:
+ fprintf (file, "bcc");
break;
-
- case CONST_INT:
- fprintf (file, "#%d", ((-INTVAL (x)) & 0xff00) >> 8);
+ case EQ:
+ fprintf (file, "bcs");
break;
-
default:
abort ();
}
break;
-
case 'j':
asm_fprintf (file, cond_string (GET_CODE (x)));
break;
-
case 'k':
asm_fprintf (file, cond_string (reverse_condition (GET_CODE (x))));
break;
- def: ;
+ case 's':
+ if (GET_CODE (x) == CONST_INT)
+ fprintf (file, "#%d", (INTVAL (x)) & 0xff);
+ else
+ fprintf (file, "%s", byte_reg (x, 0));
+ break;
+ case 't':
+ if (GET_CODE (x) == CONST_INT)
+ fprintf (file, "#%d", (INTVAL (x) >> 8) & 0xff);
+ else
+ fprintf (file, "%s", byte_reg (x, 1));
+ break;
+ case 'u':
+ if (GET_CODE (x) != CONST_INT)
+ abort ();
+ fprintf (file, "%d", INTVAL (x));
+ break;
+ case 'w':
+ if (GET_CODE (x) == CONST_INT)
+ fprintf (file, "#%d", INTVAL (x) & 0xff);
+ else
+ fprintf (file, "%s", byte_reg (x, TARGET_H8300 ? 2 : 0));
+ break;
+ case 'x':
+ if (GET_CODE (x) == CONST_INT)
+ fprintf (file, "#%d", (INTVAL (x) >> 8) & 0xff);
+ else
+ fprintf (file, "%s", byte_reg (x, TARGET_H8300 ? 3 : 1));
+ break;
+ case 'y':
+ if (GET_CODE (x) == CONST_INT)
+ fprintf (file, "#%d", (INTVAL (x) >> 16) & 0xff);
+ else
+ fprintf (file, "%s", byte_reg (x, 0));
+ break;
+ case 'z':
+ if (GET_CODE (x) == CONST_INT)
+ fprintf (file, "#%d", (INTVAL (x) >> 24) & 0xff);
+ else
+ fprintf (file, "%s", byte_reg (x, 1));
+ break;
+
default:
+ def:
switch (GET_CODE (x))
{
case REG:
- fprintf (file, "%s", names_big[REGNO (x)]);
+ switch (GET_MODE (x))
+ {
+ case QImode:
+#if 0 /* Is it asm ("mov.b %0,r2l", ...) */
+ fprintf (file, "%s", byte_reg (x, 0));
+#else /* ... or is it asm ("mov.b %0l,r2l", ...) */
+ fprintf (file, "%s", names_big[REGNO (x)]);
+#endif
+ break;
+ case HImode:
+ fprintf (file, "%s", names_big[REGNO (x)]);
+ break;
+ case SImode:
+ fprintf (file, "%s", names_extended[REGNO (x)]);
+ break;
+ default:
+ abort ();
+ }
break;
case MEM:
@@ -720,15 +1139,15 @@ print_operand_address (file, addr)
switch (GET_CODE (addr))
{
case REG:
- fprintf (file, "%s", names_big[REGNO (addr)]);
+ fprintf (file, "%s", h8_reg_names[REGNO (addr)]);
break;
case PRE_DEC:
- fprintf (file, "-%s", names_big[REGNO (XEXP (addr, 0))]);
+ fprintf (file, "-%s", h8_reg_names[REGNO (XEXP (addr, 0))]);
break;
case POST_INC:
- fprintf (file, "%s+", names_big[REGNO (XEXP (addr, 0))]);
+ fprintf (file, "%s+", h8_reg_names[REGNO (XEXP (addr, 0))]);
break;
case PLUS:
@@ -751,15 +1170,22 @@ print_operand_address (file, addr)
break;
case CONST_INT:
- if (INTVAL (addr) < 0)
- {
- int v = -INTVAL (addr);
-
- fprintf (file, "-%d", v);
- }
- else
- fprintf (file, "%d", INTVAL (addr));
- break;
+ {
+ /* Since the h8/300 only has 16 bit pointers, negative values are also
+ those >= 32768. This happens for example with pointer minus a
+ constant. We don't want to turn (char *p - 2) into
+ (char *p + 65534) because loop unrolling can build upon this
+ (IE: char *p + 131068). */
+ int n = INTVAL (addr);
+ if (TARGET_H8300)
+ n = (int) (short) n;
+ if (n < 0)
+ /* ??? Why the special case for -ve values? */
+ fprintf (file, "-%d", -n);
+ else
+ fprintf (file, "%d", n);
+ break;
+ }
default:
output_addr_const (file, addr);
@@ -782,139 +1208,833 @@ final_prescan_insn (insn, operand, num_operands)
int uid = INSN_UID (insn);
+ if (TARGET_RTL_DUMP)
+ {
+ fprintf (asm_out_file, "\n****************");
+ print_rtl (asm_out_file, PATTERN (insn));
+ fprintf (asm_out_file, "\n");
+ }
+
if (TARGET_ADDRESSES)
{
- fprintf (asm_out_file, "; %d %d\n", insn_addresses[uid],
+ fprintf (asm_out_file, "; 0x%x %d\n", insn_addresses[uid],
insn_addresses[uid] - last_insn_address);
last_insn_address = insn_addresses[uid];
}
}
-static void
-shift_n_bits (lval, operand, f, notzero)
- rtx lval;
- rtx operand;
- rtx (*f) ();
- int notzero;
+/* Prepare for an SI sized move. */
+
+int
+do_movsi (operands)
+ rtx operands[];
{
- rtx label = gen_label_rtx ();
- rtx bot = gen_label_rtx ();
+ rtx src = operands[1];
+ rtx dst = operands[0];
+ if (!reload_in_progress && !reload_completed)
+ {
+ if (!register_operand (dst, GET_MODE (dst)))
+ {
+ rtx tmp = gen_reg_rtx (GET_MODE (dst));
+ emit_move_insn (tmp, src);
+ operands[1] = tmp;
+ }
+ }
+ return 0;
+}
+
+/* Function for INITIAL_ELIMINATION_OFFSET(FROM, TO, OFFSET).
+ Define the offset between two registers, one to be eliminated, and the other
+ its replacement, at the start of a routine. */
+
+int
+initial_offset (from, to)
+{
+ int offset = 0;
+
+ if (from == ARG_POINTER_REGNUM && to == FRAME_POINTER_REGNUM)
+ offset = UNITS_PER_WORD + frame_pointer_needed * UNITS_PER_WORD;
+ else
+ {
+ int regno;
+
+ for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
+ if ((regs_ever_live[regno]
+ && (!call_used_regs[regno] || regno == FRAME_POINTER_REGNUM)))
+ offset += UNITS_PER_WORD;
- if (! notzero)
+ /* See the comments for get_frame_size. We need to round it up to
+ STACK_BOUNDARY. */
+
+ offset += ((get_frame_size () + STACK_BOUNDARY / BITS_PER_UNIT - 1)
+ & ~(STACK_BOUNDARY / BITS_PER_UNIT - 1));
+
+ if (from == ARG_POINTER_REGNUM && to == STACK_POINTER_REGNUM)
+ offset += UNITS_PER_WORD; /* Skip saved PC */
+ }
+ return offset;
+}
+
+/* Update the condition code from the insn. */
+
+int
+notice_update_cc (body, insn)
+ rtx body;
+ rtx insn;
+{
+ switch (get_attr_cc (insn))
{
- /* Have to put a zero test at the top. */
- emit_insn (gen_rtx (SET, VOIDmode, cc0_rtx, lval));
- emit_jump_insn (gen_beq (bot));
+ case CC_NONE:
+ /* Insn does not affect the CC at all */
+ break;
+
+ case CC_NONE_0HIT:
+ /* Insn does not change the CC, but the 0't operand has been changed. */
+
+ if (cc_status.value1 != 0
+ && reg_overlap_mentioned_p (recog_operand[0], cc_status.value1))
+ cc_status.value1 = 0;
+
+ if (cc_status.value2 != 0
+ && reg_overlap_mentioned_p (recog_operand[0], cc_status.value2))
+ cc_status.value2 = 0;
+
+ break;
+
+ case CC_SET:
+ /* Insn sets CC to recog_operand[0], but overflow is impossible. */
+ CC_STATUS_INIT;
+ cc_status.flags |= CC_NO_OVERFLOW;
+ cc_status.value1 = recog_operand[0];
+ break;
+
+ case CC_COMPARE:
+ /* The insn is a compare instruction */
+ CC_STATUS_INIT;
+ cc_status.value1 = SET_SRC (body);
+ break;
+
+ case CC_CBIT:
+ CC_STATUS_INIT;
+ cc_status.flags |= CC_DONE_CBIT;
+ cc_status.value1 = 0;
+ break;
+
+ case CC_WHOOPS:
+ case CC_CLOBBER:
+ /* Insn clobbers CC. */
+ CC_STATUS_INIT;
+ break;
}
- emit_label (label);
- f (operand);
- emit_insn (gen_rtx (SET, QImode, lval,
- gen_rtx (MINUS, QImode, lval, const1_rtx)));
- emit_insn (gen_rtx (SET, VOIDmode, cc0_rtx, lval));
- emit_jump_insn (gen_bne (label));
+}
+
+/* Recognize valid operators for bit instructions */
- emit_label (bot);
- /* We can't end an expand with a label. */
- emit_move_insn (operand, operand);
+int
+bit_operator (x, mode)
+ rtx x;
+ enum machine_mode mode;
+{
+ enum rtx_code code = GET_CODE (x);
+
+ return (code == XOR
+ || code == AND
+ || code == IOR);
}
+
+/* Shifts.
+
+ We devote a fair bit of code to getting efficient shifts since we can only
+ shift one bit at a time. See the .md file for more comments.
+
+ Here are some thoughts on what the absolutely positively best code is.
+ "Best" here means some rational trade-off between code size and speed,
+ where speed is more preferred but not at the expense of generating 20 insns.
+
+ H8/300 QImode shifts
+ 1-4 - do them inline
+ 5-6 - ASHIFT | LSHIFTRT: rotate, mask off other bits
+ ASHIFTRT: loop
+ 7 - ASHIFT | LSHIFTRT: rotate, mask off other bits
+ ASHIFTRT: shll, subx (propagate carry bit to all bits)
+
+ H8/300 HImode shifts
+ 1-4 - do them inline
+ 5-6 - loop
+ 7 - shift other way once, move byte into place, move carry bit into place
+ 8 - move byte, zero (ASHIFT | LSHIFTRT) or sign extend other (ASHIFTRT)
+ 9 - inline shift 1-4, move byte, set other byte
+ 13-14 - ASHIFT | LSHIFTRT: rotate 3/2, mask, move byte, set other byte to 0
+ - ASHIFTRT: loop
+ 15 - ASHIFT | LSHIFTRT: rotate 1, mask, move byte, set other byte to 0
+ - ASHIFTRT: shll, subx, set other byte
+
+ H8/300 SImode shifts
+ 1-2 - do them inline
+ 3-6 - loop
+ 7 - shift other way once, move bytes into place,
+ move carry into place (possibly with sign extension)
+ 8 - move bytes into place, zero or sign extend other
+ 9-14 - loop
+ 15 - shift other way once, move word into place, move carry into place
+ 16 - move word, zero or sign extend other
+ 17-23 - loop
+ 24 - move bytes into place, zero or sign extend other
+ 25-27 - loop
+ 28-30 - ASHIFT | LSHIFTRT: rotate top byte, mask, move byte into place,
+ zero others
+ ASHIFTRT: loop
+ 31 - ASHIFT | LSHIFTRT: rotate top byte, mask, byte byte into place,
+ zero others
+ ASHIFTRT: shll top byte, subx, copy to other bytes
+
+ H8/300H QImode shifts
+ - same as H8/300
+
+ H8/300H HImode shifts
+ - same as H8/300
+
+ H8/300H SImode shifts
+ (These are complicated by the fact that we don't have byte level access to
+ the top word.)
+ A word is: bytes 3,2,1,0 (msb -> lsb), word 1,0 (msw -> lsw)
+ 1-4 - do them inline
+ 5-14 - loop
+ 15 - shift other way once, move word into place, move carry into place
+ (with sign extension for ASHIFTRT)
+ 16 - move word into place, zero or sign extend other
+ 17-23 - loop
+ 24 - ASHIFT: move byte 0(msb) to byte 1, zero byte 0,
+ move word 0 to word 1, zero word 0
+ LSHIFTRT: move word 1 to word 0, move byte 1 to byte 0,
+ zero word 1, zero byte 1
+ ASHIFTRT: move word 1 to word 0, move byte 1 to byte 0,
+ sign extend byte 0, sign extend word 0
+ 25-27 - either loop, or
+ do 24 bit shift, inline rest
+ 28-30 - ASHIFT: rotate 4/3/2, mask
+ LSHIFTRT: rotate 4/3/2, mask
+ ASHIFTRT: loop
+ 31 - shll, subx byte 0, sign extend byte 0, sign extend word 0
+
+ Don't Panic!!!
+
+ All of these haven't been implemented. I've just documented them and
+ provided hooks so they can be.
+*/
int
-can_shift (code, operands, f, limit, fby_eight)
+nshift_operator (x, mode)
+ rtx x;
+ enum machine_mode mode;
+{
+ switch (GET_CODE (x))
+ {
+ case ASHIFTRT:
+ case LSHIFTRT:
+ case ASHIFT:
+ return 1;
+
+ default:
+ return 0;
+ }
+}
+
+/* Called from the .md file to emit code to do shifts.
+ Returns a boolean indicating success
+ (currently this is always TRUE). */
+
+int
+expand_a_shift (mode, code, operands)
+ enum machine_mode mode;
int code;
rtx operands[];
- rtx (*f) ();
- int limit;
- rtx (*fby_eight) ();
{
extern int rtx_equal_function_value_matters;
emit_move_insn (operands[0], operands[1]);
- if (GET_CODE (operands[2]) != CONST_INT)
- {
- rtx lval;
+ /* need a loop to get all the bits we want - we generate the
+ code at emit time, but need to allocate a scratch reg now */
+
+ emit_insn (gen_rtx
+ (PARALLEL, VOIDmode,
+ gen_rtvec (2,
+ gen_rtx (SET, VOIDmode, operands[0],
+ gen_rtx (code, mode, operands[0], operands[2])),
+ gen_rtx (CLOBBER, VOIDmode, gen_rtx (SCRATCH, QImode, 0)))));
- /* Can't define expand a loop after rtl generation. */
- if (! rtx_equal_function_value_matters)
- return 0;
+ return 1;
+}
+
+/* Shift algorithm determination.
+
+ There are various ways of doing a shift:
+ SHIFT_INLINE: If the amount is small enough, just generate as many one-bit
+ shifts as we need.
+ SHIFT_ROT_AND: If the amount is large but close to either end, rotate the
+ necessary bits into position and then set the rest to zero.
+ SHIFT_SPECIAL: Hand crafted assembler.
+ SHIFT_LOOP: If the above methods fail, just loop. */
+
+enum shift_alg
+{
+ SHIFT_INLINE,
+ SHIFT_ROT_AND,
+ SHIFT_SPECIAL,
+ SHIFT_LOOP,
+ SHIFT_MAX
+};
- lval = gen_reg_rtx (QImode);
+/* Symbols of the various shifts which can be used as indices. */
- convert_move (lval, operands[2], 1);
- shift_n_bits (lval, operands[0], f, 0);
+enum shift_type
+ {
+ SHIFT_ASHIFT, SHIFT_LSHIFTRT, SHIFT_ASHIFTRT
+ };
+
+/* Symbols of the various modes which can be used as indices. */
+
+enum shift_mode
+ {
+ QIshift, HIshift, SIshift
+ };
+
+/* For single bit shift insns, record assembler and whether the condition code
+ is valid afterwards. */
+
+struct shift_insn
+{
+ char *assembler;
+ int cc_valid;
+};
+
+/* Assembler instruction shift table.
+
+ These tables are used to look up the basic shifts.
+ They are indexed by cpu, shift_type, and mode.
+*/
+
+static const struct shift_insn shift_one[2][3][3] =
+{
+/* H8/300 */
+ {
+/* SHIFT_ASHIFT */
+ {
+ { "shal %X0", 1 },
+ { "add.w %T0,%T0\t; shal.w", 1 },
+ { "add.w %f0,%f0\t; shal.l\n\taddx %y0,%y0\n\taddx %z0,%z0\t; end shal.l", 0 }
+ },
+/* SHIFT_LSHIFTRT */
+ {
+ { "shlr %X0", 1 },
+ { "shlr %t0\t; shlr.w\n\trotxr %s0\t; end shlr.w", 0 },
+ { "shlr %z0\t; shlr.l\n\trotxr %y0\n\trotxr %x0\n\trotxr %w0\t; end shlr.l", 0 }
+ },
+/* SHIFT_ASHIFTRT */
+ {
+ { "shar %X0", 1 },
+ { "shar %t0\t; shar.w\n\trotxr %s0\t; end shar.w", 0 },
+ { "shar %z0\t; shar.l\n\trotxr %y0\n\trotxr %x0\n\trotxr %w0\t; end shar.l", 0 }
}
- else
+ },
+/* H8/300H */
+ {
+/* SHIFT_ASHIFT */
+ {
+ { "shal.b %X0", 1 },
+ { "shal.w %T0", 1 },
+ { "shal.l %S0", 1 }
+ },
+/* SHIFT_LSHIFTRT */
+ {
+ { "shlr.b %X0", 1 },
+ { "shlr.w %T0", 1 },
+ { "shlr.l %S0", 1 }
+ },
+/* SHIFT_ASHIFTRT */
+ {
+ { "shar.b %X0", 1 },
+ { "shar.w %T0", 1 },
+ { "shar.l %S0", 1 }
+ }
+ }
+};
+
+/* Rotates are organized by which shift they'll be used in implementing.
+ There's no need to record whether the cc is valid afterwards because
+ it is the AND insn that will decide this. */
+
+static const char *const rotate_one[2][3][3] =
+{
+/* H8/300 */
+ {
+/* SHIFT_ASHIFT */
+ {
+ "rotr %X0",
+ "shlr %t0\t; rotr.w\n\trotxr %s0\n\tbst #7,%t0\t; end rotr.w",
+ 0
+ },
+/* SHIFT_LSHIFTRT */
+ {
+ "rotl %X0",
+ "shll %s0\t; rotl.w\n\trotxl %t0\n\tbst #0,%s0\t; end rotl.w",
+ 0
+ },
+/* SHIFT_ASHIFTRT */
+ {
+ "rotl %X0",
+ "shll %s0\t; rotl.w\n\trotxl %t0\n\tbst #0,%s0\t; end rotl.w",
+ 0
+ }
+ },
+/* H8/300H */
+ {
+/* SHIFT_ASHIFT */
+ {
+ "rotr.b %X0",
+ "rotr.w %T0",
+ "rotr.l %S0"
+ },
+/* SHIFT_LSHIFTRT */
+ {
+ "rotl.b %X0",
+ "rotl.w %T0",
+ "rotl.l %S0"
+ },
+/* SHIFT_ASHIFTRT */
+ {
+ "rotl.b %X0",
+ "rotl.w %T0",
+ "rotl.l %S0"
+ }
+ }
+};
+
+/* Given CPU, MODE, SHIFT_TYPE, and shift count COUNT, determine the best
+ algorithm for doing the shift. The assembler code is stored in ASSEMBLER.
+ We don't achieve maximum efficiency in all cases, but the hooks are here
+ to do so.
+
+ For now we just use lots of switch statements. Since we don't even come
+ close to supporting all the cases, this is simplest. If this function ever
+ gets too big, perhaps resort to a more table based lookup. Of course,
+ at this point you may just wish to do it all in rtl.
+
+ WARNING: The constraints on insns shiftbyn_QI/HI/SI assume shifts of
+ 1,2,3,4 will be inlined (1,2 for SI). */
+
+static enum shift_alg
+get_shift_alg (cpu, shift_type, mode, count, assembler_p, cc_valid_p)
+ enum attr_cpu cpu;
+ enum shift_type shift_type;
+ enum machine_mode mode;
+ int count;
+ const char **assembler_p;
+ int *cc_valid_p;
+{
+ /* The default is to loop. */
+ enum shift_alg alg = SHIFT_LOOP;
+ enum shift_mode shift_mode;
+
+ /* We don't handle negative shifts or shifts greater than the word size,
+ they should have been handled already. */
+
+ if (count < 0 || count > GET_MODE_BITSIZE (mode))
+ abort ();
+
+ switch (mode)
{
- int i;
+ case QImode:
+ shift_mode = QIshift;
+ break;
+ case HImode:
+ shift_mode = HIshift;
+ break;
+ case SImode:
+ shift_mode = SIshift;
+ break;
+ default:
+ abort ();
+ }
+
+ /* Assume either SHIFT_LOOP or SHIFT_INLINE.
+ It is up to the caller to know that looping clobbers cc. */
+ *assembler_p = shift_one[cpu][shift_type][shift_mode].assembler;
+ *cc_valid_p = shift_one[cpu][shift_type][shift_mode].cc_valid;
- i = INTVAL (operands[2]);
- if (i >= 8 && fby_eight)
+ /* Now look for cases we want to optimize. */
+
+ switch (shift_mode)
+ {
+ case QIshift:
+ if (count <= 4)
+ return SHIFT_INLINE;
+ else if (count <= 6)
{
- fby_eight (operands[0]);
- i -= 8;
+ if (shift_type == SHIFT_ASHIFTRT)
+ {
+ return SHIFT_LOOP;
+ }
+ else
+ {
+ *assembler_p = rotate_one[cpu][shift_type][shift_mode];
+ *cc_valid_p = 0;
+ return SHIFT_ROT_AND;
+ }
}
- if (i > limit)
+ else if (count == 7)
{
- rtx lval;
-
- /* Can't define expand a loop after rtl generation. */
- if (! rtx_equal_function_value_matters)
- return 0;
- lval = gen_reg_rtx (QImode);
- emit_move_insn (lval, gen_rtx (CONST_INT, VOIDmode, i));
- shift_n_bits (lval, operands[0], f, 1);
+ if (shift_type == SHIFT_ASHIFTRT)
+ {
+ *assembler_p = "shll %X0\t; shar.b(7)\n\tsubx %X0,%X0\t; end shar.b(7)";
+ *cc_valid_p = 0;
+ return SHIFT_SPECIAL;
+ }
+ else
+ {
+ *assembler_p = rotate_one[cpu][shift_type][shift_mode];
+ *cc_valid_p = 0;
+ return SHIFT_ROT_AND;
+ }
}
- else
+ break;
+ case HIshift:
+ if (count <= 4)
+ return SHIFT_INLINE;
+ else if (count == 8)
+ {
+ switch (shift_type)
+ {
+ case SHIFT_ASHIFT:
+ *assembler_p = "mov.b %s0,%t0\t; shal.w(8)\n\tsub.b %s0,%s0\t; end shal.w(8)";
+ *cc_valid_p = 0;
+ return SHIFT_SPECIAL;
+ case SHIFT_LSHIFTRT:
+ *assembler_p = "mov.b %t0,%s0\t; shlr.w(8)\n\tsub.b %t0,%t0\t; end shlr.w(8)";
+ *cc_valid_p = 0;
+ return SHIFT_SPECIAL;
+ case SHIFT_ASHIFTRT:
+ if (cpu == CPU_H8300)
+ *assembler_p = "mov.b %t0,%s0\t; shar.w(8)\n\tshll %t0\n\tsubx %t0,%t0\t; end shar.w(8)";
+ else
+ *assembler_p = "mov.b %t0,%s0\t; shar.w(8)\n\texts.w %T0\t; end shar.w(8)";
+ *cc_valid_p = 0;
+ return SHIFT_SPECIAL;
+ }
+ abort ();
+ }
+ else if (count == 15)
+ {
+ if (shift_type == SHIFT_ASHIFTRT)
+ {
+ *assembler_p = "shll %t0,%t0\t; shar.w(15)\n\tsubx %t0,%t0\n\tmov.b %t0,%s0\t; end shar.w(15)";
+ *cc_valid_p = 0;
+ return SHIFT_SPECIAL;
+ }
+ else
+ {
+ *assembler_p = rotate_one[cpu][shift_type][shift_mode];
+ *cc_valid_p = 0;
+ return SHIFT_ROT_AND;
+ }
+ }
+ break;
+ case SIshift:
+ if (count <= (cpu == CPU_H8300 ? 2 : 4))
+ return SHIFT_INLINE;
+ else if (count == 8)
+ {
+ if (cpu == CPU_H8300)
+ {
+ switch (shift_type)
+ {
+ case SHIFT_ASHIFT:
+ *assembler_p = "mov.b %y0,%z0\t; shal.l(8)\n\tmov.b %x0,%y0\n\tmov.b %w0,%x0\n\tsub.b %w0,%w0\t; end shal.l(8)";
+ *cc_valid_p = 0;
+ return SHIFT_SPECIAL;
+ case SHIFT_LSHIFTRT:
+ *assembler_p = "mov.b %x0,%w0\t; shlr.l(8)\n\tmov.b %y0,%x0\n\tmov.b %z0,%y0\n\tsub.b %z0,%z0\t; end shlr.l(8)";
+ *cc_valid_p = 0;
+ return SHIFT_SPECIAL;
+ case SHIFT_ASHIFTRT:
+ *assembler_p = "mov.b %x0,%w0\t; shar.l(8)\n\tmov.b %y0,%x0\n\tmov.b %z0,%y0\n\tshll %z0\n\tsubx %z0,%z0; end shar.l(8)";
+ *cc_valid_p = 0;
+ return SHIFT_SPECIAL;
+ }
+ }
+ else /* CPU_H8300H */
+ /* We don't have byte level access to the high word so this isn't
+ easy to do. For now, just loop. */
+ ;
+ }
+ else if (count == 16)
{
- while (i--)
- f (operands[0]);
+ switch (shift_type)
+ {
+ case SHIFT_ASHIFT:
+ *assembler_p = "mov.w %f0,%e0\t; shal.l(16)\n\tsub.w %f0,%f0\t; end shal.l(16)";
+ *cc_valid_p = 0;
+ return SHIFT_SPECIAL;
+ case SHIFT_LSHIFTRT:
+ *assembler_p = "mov.w %e0,%f0\t; shlr.l(16)\n\tsub.w %e0,%e0\t; end shlr.l(16)";
+ *cc_valid_p = 0;
+ return SHIFT_SPECIAL;
+ case SHIFT_ASHIFTRT:
+ if (cpu == CPU_H8300)
+ *assembler_p = "mov.w %e0,%f0\t; shar.l(16)\n\tshll %z0\n\tsubx %z0,%z0\n\tmov.b %z0,%y0\t; end shar.l(16)";
+ else
+ *assembler_p = "mov.w %e0,%f0\t; shar.l(16)\n\texts.l %S0\t; end shar.l(16)";
+ *cc_valid_p = 0;
+ return SHIFT_SPECIAL;
+ }
}
+ else if (count >= 28 && count <= 30)
+ {
+ if (shift_type == SHIFT_ASHIFTRT)
+ {
+ return SHIFT_LOOP;
+ }
+ else
+ {
+ if (cpu == CPU_H8300)
+ return SHIFT_LOOP;
+ else
+ {
+ *assembler_p = rotate_one[cpu][shift_type][shift_mode];
+ *cc_valid_p = 0;
+ return SHIFT_ROT_AND;
+ }
+ }
+ }
+ else if (count == 31)
+ {
+ if (shift_type == SHIFT_ASHIFTRT)
+ {
+ if (cpu == CPU_H8300)
+ *assembler_p = "shll %z0\t; shar.l(31)\n\tsubx %w0,%w0\n\tmov.b %w0,%x0\n\tmov.w %f0,%e0\t; end shar.l(31)";
+ else
+ *assembler_p = "shll %e0\t; shar.l(31)\n\tsubx %w0,%w0\n\tmov.b %w0,%x0\n\tmov.w %f0,%e0\t; end shar.l(31)";
+ *cc_valid_p = 0;
+ return SHIFT_SPECIAL;
+ }
+ else
+ {
+ if (cpu == CPU_H8300)
+ {
+ if (shift_type == SHIFT_ASHIFT)
+ *assembler_p = "sub.w %e0,%e0\t; shal.l(31)\n\tshlr %w0\n\tmov.w %e0,%f0\n\trotxr %z0\t; end shal.l(31)";
+ else
+ *assembler_p = "sub.w %f0,%f0\t; shlr.l(31)\n\tshll %z0\n\tmov.w %f0,%e0\n\trotxl %w0\t; end shlr.l(31)";
+ *cc_valid_p = 0;
+ return SHIFT_SPECIAL;
+ }
+ else
+ {
+ *assembler_p = rotate_one[cpu][shift_type][shift_mode];
+ *cc_valid_p = 0;
+ return SHIFT_ROT_AND;
+ }
+ }
+ }
+ break;
+ default:
+ abort ();
}
- return 1;
+
+ return alg;
}
-int
-domovsi (operands)
- rtx operands[];
+/* Emit the assembler code for doing shifts. */
+
+char *
+emit_a_shift (insn, operands)
+ rtx insn;
+ rtx *operands;
{
- rtx src = operands[1];
- rtx dst = operands[0];
+ static int loopend_lab;
+ char *assembler;
+ int cc_valid;
+ rtx inside = PATTERN (insn);
+ rtx shift = operands[3];
+ enum machine_mode mode = GET_MODE (shift);
+ enum rtx_code code = GET_CODE (shift);
+ enum shift_type shift_type;
+ enum shift_mode shift_mode;
+
+ loopend_lab++;
+
+ switch (mode)
+ {
+ case QImode:
+ shift_mode = QIshift;
+ break;
+ case HImode:
+ shift_mode = HIshift;
+ break;
+ case SImode:
+ shift_mode = SIshift;
+ break;
+ default:
+ abort ();
+ }
- if (push_operand (dst, GET_MODE (dst)))
+ switch (code)
{
- /* Source must be a reg. */
- if (! REG_P (src))
- {
- rtx tmp = gen_reg_rtx (GET_MODE (dst));
+ case ASHIFTRT:
+ shift_type = SHIFT_ASHIFTRT;
+ break;
+ case LSHIFTRT:
+ shift_type = SHIFT_LSHIFTRT;
+ break;
+ case ASHIFT:
+ shift_type = SHIFT_ASHIFT;
+ break;
+ default:
+ abort ();
+ }
- emit_move_insn (tmp, src);
- operands[1] = tmp;
+ if (GET_CODE (operands[2]) != CONST_INT)
+ {
+ /* Indexing by reg, so have to loop and test at top */
+ output_asm_insn ("mov.b %X2,%X4", operands);
+ fprintf (asm_out_file, "\tble .Lle%d\n", loopend_lab);
+
+ /* Get the assembler code to do one shift. */
+ get_shift_alg (cpu_type, shift_type, mode, 1, &assembler, &cc_valid);
+ }
+ else
+ {
+ int n = INTVAL (operands[2]);
+ enum shift_alg alg;
+
+ /* If the count is negative, make it 0. */
+ if (n < 0)
+ n = 0;
+ /* If the count is too big, truncate it.
+ ANSI says shifts of GET_MODE_BITSIZE are undefined - we choose to
+ do the intuitive thing. */
+ else if (n > GET_MODE_BITSIZE (mode))
+ n = GET_MODE_BITSIZE (mode);
+
+ alg = get_shift_alg (cpu_type, shift_type, mode, n, &assembler, &cc_valid);
+
+ switch (alg)
+ {
+ case SHIFT_INLINE:
+ while (--n >= 0)
+ output_asm_insn (assembler, operands);
+ if (cc_valid)
+ cc_status.value1 = operands[0];
+ return "";
+ case SHIFT_ROT_AND:
+ {
+ int m = GET_MODE_BITSIZE (mode) - n;
+ int mask = (shift_type == SHIFT_ASHIFT
+ ? ((1 << GET_MODE_BITSIZE (mode) - n) - 1) << n
+ : (1 << GET_MODE_BITSIZE (mode) - n) - 1);
+ char insn_buf[200];
+ /* Not all possibilities of rotate are supported. They shouldn't
+ be generated, but let's watch for 'em. */
+ if (assembler == 0)
+ abort ();
+ while (--m >= 0)
+ output_asm_insn (assembler, operands);
+ if (TARGET_H8300)
+ {
+ switch (mode)
+ {
+ case QImode:
+ sprintf (insn_buf, "and #%d,%%X0\t; end shift %d via rotate+and",
+ mask, n);
+ cc_status.value1 = operands[0];
+ break;
+ case HImode:
+ sprintf (insn_buf, "and #%d,%%s0\n\tand #%d,%%t0\t; end shift %d via rotate+and",
+ mask & 255, mask >> 8, n);
+ break;
+ case SImode:
+ abort ();
+ }
+ }
+ else
+ {
+ sprintf (insn_buf, "and.%c #%d,%%%c0",
+ "bwl"[shift_mode], mask,
+ mode == QImode ? 'X' : mode == HImode ? 'T' : 'S');
+ cc_status.value1 = operands[0];
+ }
+ output_asm_insn (insn_buf, operands);
+ return "";
+ }
+ case SHIFT_SPECIAL:
+ output_asm_insn (assembler, operands);
+ return "";
}
+
+ /* Need a loop, move limit to tmp reg */
+ fprintf (asm_out_file, "\tmov.b #%d,%sl\n", n, names_big[REGNO (operands[4])]);
}
- return 0;
+
+ fprintf (asm_out_file, ".Llt%d:\n", loopend_lab);
+ output_asm_insn (assembler, operands);
+ output_asm_insn ("add #0xff,%X4", operands);
+ fprintf (asm_out_file, "\tbne .Llt%d\n", loopend_lab);
+ fprintf (asm_out_file, ".Lle%d:\n", loopend_lab);
+
+ return "";
}
+
+/* Fix the operands of a gen_xxx so that it could become a bit
+ operating insn. */
int
-io (FROM, TO)
+fix_bit_operand (operands, what, type)
+ rtx *operands;
+ char what;
+ enum rtx_code type;
{
- int OFFSET = 0;
+ /* The bit_operand predicate accepts any memory durint RTL generation, but
+ only 'U' memory afterwards, so if this is a MEM operand, we must force
+ it to be valid for 'U' by reloading the address. */
- if ((FROM) == ARG_POINTER_REGNUM && (TO) == FRAME_POINTER_REGNUM)
- (OFFSET) = 2 + frame_pointer_needed * 2;
- else
+ if (GET_CODE (operands[2]) == CONST_INT)
{
- int regno;
- int offset = 0;
-
- for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
- if ((regs_ever_live[regno]
- && (! call_used_regs[regno] || regno == FRAME_POINTER_REGNUM)))
- offset += 2;
+ if (CONST_OK_FOR_LETTER_P (INTVAL (operands[2]), what))
+ {
+ /* Ok to have a memory dest. */
+ if (GET_CODE (operands[0]) == MEM && !EXTRA_CONSTRAINT (operands[0], 'U'))
+ {
+ rtx mem;
+ mem = gen_rtx (MEM, GET_MODE (operands[0]),
+ copy_to_mode_reg (Pmode, XEXP (operands[0], 0)));
+ RTX_UNCHANGING_P (mem) = RTX_UNCHANGING_P (operands[0]);
+ MEM_IN_STRUCT_P (mem) = MEM_IN_STRUCT_P (operands[0]);
+ MEM_VOLATILE_P (mem) = MEM_VOLATILE_P (operands[0]);
+ operands[0] = mem;
+ }
+
+ if (GET_CODE (operands[1]) == MEM && !EXTRA_CONSTRAINT (operands[1], 'U'))
+ {
+ rtx mem;
+ mem = gen_rtx (MEM, GET_MODE (operands[1]),
+ copy_to_mode_reg (Pmode, XEXP (operands[1], 0)));
+ RTX_UNCHANGING_P (mem) = RTX_UNCHANGING_P (operands[1]);
+ MEM_IN_STRUCT_P (mem) = MEM_IN_STRUCT_P (operands[1]);
+ MEM_VOLATILE_P (mem) = MEM_VOLATILE_P (operands[1]);
+ operands[1] = mem;
+ }
+ return 0;
+ }
+ }
- (OFFSET) = offset + get_frame_size ();
+ /* Dest and src op must be register. */
- if ((FROM) == ARG_POINTER_REGNUM && (TO) == STACK_POINTER_REGNUM)
- (OFFSET) += 2; /* Skip saved PC. */
- }
- return OFFSET;
+ operands[1] = force_reg (QImode, operands[1]);
+ {
+ rtx res = gen_reg_rtx (QImode);
+ emit_insn (gen_rtx (SET, VOIDmode, res, gen_rtx (type, QImode, operands[1], operands[2])));
+ emit_insn (gen_rtx (SET, VOIDmode, operands[0], res));
+ }
+ return 1;
}